Reading notes: Programming techniques for improving C + + performance

Source: Internet
Author: User

1TH. Tracking example 1.1 concerns

The practical problem introduced in this chapter is to define a simple trace class that outputs the current function name to a log file. Trace objects bring some overhead, so the trace feature is not turned on by default. The question is: How to design the trace class so that the least cost is introduced when the trace function is not turned on.

1.2 Using the state variable switch function

Switching the trace function with a macro is simple and does not cost at all when it is not turned on:

#ifdef tracetrace Trace ("AAA"); #endif

The disadvantage is that each switch needs to be recompiled.

The use of state variables has some runtime overhead, but it is a reasonable choice to ensure flexibility:

Class Trace {public:    ...    static bool istraceenabled;    void Debug () {        if (istraceenabled) {            ...        }}}    

1.3 Deferred Creation

A string member is built into the original Trace class, which also assumes the overhead of construction and destruction when the trace is not opened. You can change it to string*, and then create the member when it really needs to be turned on.

This method works well if the trace is opened much less than the total time, otherwise the original method is better when the cost of dynamic creation is greater than the cost of a fixed 1 construction and destruction.

2nd. Constructors and Destructors 2.1 concerns

Inheritance and compositing result in more overhead for constructors and destructors than you expect. The tradeoff between code reusability and operational performance is worth paying attention to.

2.2 Remove redundant objects

Removing unnecessary object usage, whether it is a member variable in a class or a function's argument, can lead to unnecessary construction and destruction overhead.

2.3 de-Duplication base class

Some base classes do not have member variables and do not provide an interface, and this base class is a meaningless base class, and removing such a base class in an inheritance hierarchy can reduce the following overhead:

    1. Overhead of construction and destruction: a function call is reduced in the corresponding procedure for each subclass.
    2. Overhead of Virtual tables: reduces the volume of each subclass object and allows the compiler to optimize them in registers.
    3. The overhead of virtual functions: in detail, the following chapters are mainly about virtual functions that cannot be inline, and the overhead of requiring additional jumps.
2.4 Trade-offs between embedded objects and embedded pointers

If you embed objects directly, the construction of object A will cause the construction of objects B and C, causing the object to B1 B2 C1 C2 ... Will form a construction tree, the overhead will be huge when the hierarchy is large.

Embedding pointers is deferred creation, constructed at first use, but with new overhead. Embedding objects directly is better if you use them frequently.

2.5 Deferred Creation

What is written here is to postpone the definition and creation of the object as much as possible until all conditions have been created.

2.6 Remove the extra construction overhead

The main point is unnecessary replication:

String s;s = "a"; There is an extra assignment overhead, and there may be a time when "a" is converted to the construction and destruction cost of a string temporary object

and initialization that does not take place in the initialization list:

Class A {public:    A (const string &s)  {        name = S;//has an extra assignment overhead    }private:    string name;};

3rd Chapter virtual function 3.1 focus point

Advantages of using good virtual functions: dynamic binding, and saving code. Try to avoid the overhead associated with virtual functions.

3.2 Overhead of virtual functions

Can be divided into three kinds:

    1. Vptr must be initialized within the constructor: the cost of building a type variable in a class that does not use virtual functions is worthwhile and not discussed.
    2. You need to use pointers to jump indirectly: the equivalent of the cost of invoking the corresponding version of the function in switch, not discussed.
    3. Virtual functions cannot be inline: this is the point of concern.
3.3 Scenario 1: Do not inherit

Does not inherit the word is to separate the subclasses, the disadvantage is that the code will be flooded with a lot of switch, very inflexible, exclude.

3.4 Scenario 2: Inheritance

The disadvantage of inheritance, as described in 3.2.1, is that member functions cannot be inline, especially with very short, frequently used functions, which adds a lot of overhead.

3.5 Scenario 3: Templates

To implement an implicit interface using a template:

Template <typename locktype>void func (LockType &lock) {    lock. Lock ();    ...    Lock. Unlock ();}

Implements an implicit interface that requires lock and unlock. Because the template is determined at compile time, the resulting function can be inline, while eliminating the overhead of a pointer-to-indirect jump.

The disadvantage is that the template-induced compilation error is very difficult to debug, while C + + does not support this implicit interface, and development often makes mistakes in the interface requirements of the template.

4th. Return Value Optimization 4.1 focus

Any time you pass through the creation and removal of objects, you will gain performance gains. The compiler removes some temporary object creation and cleanup when possible, which is known as return value optimization (RVO).

4.2 The compiler can rvo an anonymous object

Returning an anonymous object directly at the end of the function can often be rvo:

String Func () {    string a = "a";    return A;} String Funcrvo () {    return string ("a");}

Funcrvo is easier to Rvo than Func, the compiler removes the returned object and assigns its value directly to the object that receives the return value.

4.3 Active Rvo

If Class A has a "+" operation as follows:

Const a operator + (const a &x, const a &y) {    a z (x);    z + = y;    return z;}

Then add a constructor to a:

Class A {public:    A (const a &x, const a &y);//a = x + y;};

The "+" is changed to the following form for RVO benefits:

123 constoperator +(const A &x, const A &y) {    returnA(x, y);}

  

The advantage is to reduce the construction and composition of a temporary object, the disadvantage is to add a similar constructor for all operations that need to be rvo, the flexibility is too poor. If the performance requirements are particularly high, consider this optimization method.

5th. Temporary Object 5.1 focus

How to avoid creating unnecessary temporary objects.

5.2 Type Mismatch

Assigning values between different types can easily inadvertently result in the creation of temporary objects. This implicit conversion can be avoided by adding explicit before a single-argument constructor.

5.3 Avoid duplicate creation of the same temporary object

In the following loop:

Complex a;for (int i = 0; i <; ++i) {    A + = 1.0;}

Each of these loops creates a complex object with a value of 1.0. You can reduce this overhead by creating a complex object with a value of 1.0 outside the loop:

Complex One (1.0); for (int i = 0; i <; ++i) {    A + = one;}

6th. Single thread memory pool 6.1 focus

The performance of the default Universal memory Manager creates a performance bottleneck in certain scenarios. This chapter discusses a memory pool manager that performs better than general-purpose new/delete in a single-threaded environment, allocating both fixed-size and non-fixed-size memory each time.

6.2 Test Code
Rational *array[1000];for (int j = 0; J < ++j) {for    (int i = 0; i <; ++i) {        Array[i] = new ration Al (i);    }    for (int i = 0; i < ++i) {        delete array[i];}    }

6.3 Rational Dedicated Memory pool

Each time a rational-sized block of memory is allocated, the allocated free memory is maintained with an idle list, and the memory block is re-placed back into the linked list when released:

Class Rational {    ...    static List<char *> freeList;    void *operator New (size_t size) {        if (Freelist.empty ()) {            return new char[sizeof (Rational)];        } else {            void *buf = Freelist.back ();            Freelist.pop_back ();            return buf;        }    }    void operator delete (void *ptr, size_t size) {        freelist.push_back (PTR);    }};

This version of the memory pool never shrinks, and if you need to free up memory, you need to add an interface.

The benefit of this version of the memory pool compared to the common memory manager is:

    1. There is no need to handle concurrency, there is no critical section.
    2. The size of each allocation is a fixed value, without a large number of lookups in the free list (directly returning the end pointer).
6.4 Fixed-size memory pool

Instead of just for rational, it expands to support any fixed-size class:

Template <typename t>class fixedsizememorypool {public:    fixedsizememorypool (): Size_ (sizeof (T))    {}    ~fixedsizememorypool () {for        (char *&p:freelist_) {            delete[] p;        }    }    void *alloc () {        if (Freelist_.empty ()) {            return new char[size_];        } else {            void *buf = Freelist_.back () ;            Freelist_.pop_back ();            return buf;        }    }    void free (void *buf) {        freelist_.push_back (BUF);    } Private:    List<char *> freelist_;    Const size_t Size_;};

Rational would need to read:

Class Rational {public:    void *operator New (size_t size) {        return pool. Alloc ();    }    void operator delete (void *ptr, size_t size) {        pool. Free (PTR);    } Private:    static fixedsizememorypool<rational> pool;};

6.5 Variable size memory pool

The variable-size memory pool is managed differently than the previous version because there is no way to directly return a block of memory (different size) from the linked list. Here we allocate a large fixed-size block of memory when needed, allocating a large chunk of memory each time the memory of a single object is allocated from that memory block when there is not enough space.

With the increase of versatility, the performance is also declining gradually. Therefore, sacrificing some flexibility for versatility may have a good effect when performance is very much needed.

7th. Multi-threaded Memory Pool 7.1 focus

A mutex is added to a single-threaded memory pool to enable the allocator to work in a multithreaded environment. Parameters that can pass in the lock when the indexer is initialized.

7.2 Memory pool for version Pthread_mutex

Add a new template parameter in the previous chapter Mutablesizememorypool: TypeName LockType, allow incoming one locktype*, and lock on Alloc and free, others remain unchanged.

The performance of this version of the allocator is not good, because the performance of Pthread_mutex beyond our needs, we may only need a very simple function of the lock. If you can pass in a more original version of the mutex, there will be better performance.

7.3 Increasing the scalability of the memory pool

The current version of the memory pool is not performing well in high-concurrency environments, because access to memory blocks (Alloc and free) must be serialized. You can add a list of multiple memory blocks and lock each list separately, so that multiple requests can be spread across different lists and processed at the same time.

8th 8.1 Focus of the inner-linking foundation

Inline may improve performance, but it may also degrade performance. The focus of this chapter is how to avoid negative effects while taking advantage of positive returns.

8.2 Gain: Remove function calls

Eliminates the overhead of function calls. General function calls include the following:

    1. Save the value of some registers on the stack.
    2. Calculates the value of the parameter and assigns it to the corresponding stack position of the calling function.
    3. Jumps to the specified function.
    4. Saves the stack frame pointer.
    5. Executes the code.
    6. Copy the return value.
    7. Jump back to the original location.
    8. Recovery register.

In addition, the loss of processor idling due to jump is also avoided.

8.3 Benefit: cross-function optimization

Allows the compiler to perform variable optimizations across functions:

int Inc (int x) {    return x + 1;} void Func () {    int y = INC (1);}

If the above Inc is inline, Func is equivalent to:

void Func () {    int x = 1;    int y = x + 1;}

The compiler can even be further optimized to:

void Func () {    int y = 2;}

With inline, the compiler can rearrange a number of methods, omitting a large number of unnecessary statements, even including object creation and cleanup.

8.4 Benefit: Shorten critical path

By using functions on the inline critical path, you can shorten the critical path without changing the program logic at all, greatly improving performance.

8.5 Loss: Post-compilation volume

Once inline, the function code expands at each call point, which causes the code to swell if both the function code and the call point are more. On the one hand, it causes the code to load slowly, on the other hand, causes more frequent pages to occur.

But if the length of the function is particularly short, which is shorter than the entire invocation, then the inline will reduce the volume of the code, and this function must be inline.

8.6 Loss: Must be recompiled after modifying code

Inline functions If you modify the code, all the places where it is used must be recompiled, because the function's code is already expanded at each call point. So large projects tend to be inline at the end.

9th. Inline-Performance Considerations 9.1 focus

This chapter focuses on the 2nd benefit of the inline: After the code expansion of the inline function, the compiler performs optimizations for its performance.

9.2 Inter-Call optimization

After inline, the code that invokes the function is mixed with the code at the call, which allows the compiler to perform many advanced optimizations as if the code were re-organized. Especially for direct-volume optimizations:

int Choice (int x) {    switch (x) {case    1:return 5;    Case 2:    ... ...    Case 100:return 301; break;    Default:return 0; break;    }} int x = Choice (100);

After inline optimization, the above code may only be left:

int x = 301;

9.3 Why not use inline

The main disadvantage of inline is that it may increase the size of the code. In particular, the problem of volume-scale expansion occurs when a relatively large number of methods are cascaded inline.

Disadvantage 2 is that each modification requires all recompilation.

The disadvantage of 3 is that it is difficult to debug the inline function because the actual function is gone and cannot be traced to the entry and exit of the function.

9.4 Configuration-based inline

Before inline, you should count the post-compilation volume and number of calls, call points, and so on for each function to determine which functions to inline with these configuration information.

You can divide the dimensions of a function into:

    1. Static Dimensions: Number of compiled function instructions * Call points.
    2. Dynamic Size: The number of total instructions (including each call instruction and the function itself) of the function during operation * Number of calls.

The Inner alliance has great benefit to the function with small size and large dynamic size. Inline is almost always right for a function that has only one point of call, such as a call within a loop. For functions with a large number of call points and invocations, it is best to rewrite them to show their fast paths and then inline.

A function is as follows:

void Funcx {    if (/* ERROR handle code */) {        ...//lines    } ...    //real work (5 lines)}

Funcx has about 40 lines of code that are superficially inappropriate for inline, but if its error-handling code is split into a separate function:

void Funcx {    if (...)        Funcy ();    ...//real work (5 lines)}void funcy {    ...//error handle code (lines)}

At this point, Funcx has only 7 lines of code, which is good for inline. This is also equivalent to the large static size and small dynamic size of the code snippet, so that the remaining static size small dynamic size of large code can be inline.

9.5 Well-suited for inline functions
    1. Unique function: A function that has only one point of call.
    2. Small function: A function with less than 5 lines of statement.
10th. Inline Tips 10.1 focus

Some tips that can help you better inline.

10.2-piece Inline

If you want to use a precompiled option to control when certain functions are inline, and when to close inline, you can use the technique of the inline condition.

Place the definition of the inline function in the. INL, the definition of the other functions in the. cpp, and then add in. h:

#ifdef inline#include "*.inl" #endif

In the. INL, add:

#ifndef inline#define Inline  /let-inline be void#endifinline funcx (...) {}

In. cpp, add:

#ifndef inline#include "*.inl" #endif

10.3 Selective Inline

A function can be inline at some point of call and not inline at other call points. Specific content is not very like, skip.

10.4 Recursive inline

The function of tail recursion can be changed into iterative function, then find the inline method.

Non-tail recursive function if you are very concerned about performance, you can expand the function to a certain extent:

void Recursiveinline () {    ...    Recursive ();    ...} void Recursive () {    ...    Recursiveinline ();    ...}

Inline the previous function, which speeds up the run, but also significantly increases the post-compilation volume.

You can also manually expand or use macros to maintain it, but it's easy to go wrong.

10.5 Special Architectures

In some architectures (such as SPARC), the overhead of function calls is very low when the call hierarchy is small, and the benefits of the non-trivial functions are not obvious at this time. Therefore, any inline with non-trivial functions is based on understanding the configuration information.

11th. Standard Template Library 11.1 focus
    1. The performance assurance of STL and different containers and algorithms in the asymptotic complexity what's going on?
    2. STL consists of many containers, which should be used in the face of a given task?
    3. What is the performance of STL? If you develop yourself, can you do better?
11.2 Better than STL

is better than STL, often sacrifice a certain degree of versatility and flexibility, from a number of specific environmental factors to start optimization.

12th. Reference count 12.1 concerns

C + + uses reference counting to solve garbage collection problems, and the basic idea is to transfer the responsibility of object cleanup from the client code to the object itself.

Reference counting can reduce memory usage and avoid memory leaks, but it can be a disadvantage in terms of execution speed, especially in multithreaded environments.

12.2 Implementation of reference counting

Implement a: Class built-in reference count. Class Refcountbase encapsulates and references count-related operations that require classes that implement reference counting to inherit it:

Class Refcountbase {public:    Attach () {        ++refcount_;    }    Detach () {        if (--refcount_ = = 0) {            delete this;        }    } Protected:    refcountbase (): Refcount_ (0) {}    refcountbase (const refcountbase &RC): Refcount_ (0) {}    Refcountbase &operator= (const refcountbase &RC) {        return *this;    }    Virtual ~refcountbase () {}        size_t refcount_;};

If Class A inherits from Refcountbase, in order to implement the reference count, you also need a proxy class smartptr to act as a smart pointer:

Template <typename t>class smartptr {public:    smartptr (T *ptr = nullptr): Ptr_ (PTR) {}    smartptr (const Smartptr &sptr): Ptr_ (sptr.ptr_) {        if (ptr_) {            ptr_->attach ();        }    }    Smartptr &operator= (const smartptr &sptr) {        if (sptr.ptr_) {            sptr.ptr_->attach ();        }        if (ptr_)            Ptr_->detach ();        Ptr_ = sptr.ptr_;        return *this;    }    T *operator-> () {return ptr_;}    T &operator* () (return *ptr_;) Private:    T *ptr_;};

Implement B: Put the count function into the smartptr. Remove the refcountbase, instead of adding a size_t *count_ in the smartptr, ptr_ the attach operation to ++*count_, and detach to--*count_. The others are the same.

12.3 Concurrent Reference count

Smartptr need to operate on both Count_ and ptr_ in a concurrent environment, which means that locking is required before and after the operation to guarantee atomic operations on two objects.

12.4 Performance of reference counts

Implementing a requires modifications to the original class and, if this modification is not possible, you can only use implementation B. Implement b because you need to manipulate members on two heaps (Count_ and ptr_), creating and purging performance is a bit worse than implementing a.

The benefits of the reference count are:

    1. Prevent memory leaks.
    2. Efficient assignment operations. Especially as an important part of write-time replication (COW), if there are few modifications after assignment, the benefit of reference counting is very obvious compared to deep copying.
    3. Save memory space. Especially for very large objects.
    4. The RAII can be conveniently implemented. RAII can be easily implemented by turning the detach operation of the reference count into some kind of close operation.

Disadvantages of reference counting:

    1. If there are many modifications in the cow, performance is not necessarily improved compared to deep replication.
    2. There is also a lock overhead on the operation of the concurrency environment, which can affect performance more.

The following conditions increase the benefit of the reference count:

    1. The target object consumes a large amount of resources.
    2. The allocation and release of resources is expensive.
    3. The target object is highly shared.
    4. The creation and cleanup of references is inexpensive.
13th. Code Optimization 13.1 Focus

The application coding phase introduces a number of performance issues, which are usually small-scale problems, which do not require too much code to look at, and do not require a deep-seated design, but may lead to significant performance gains.

13.2 Cache

Remember to calculate frequently and calculate expensive calculations. Typically, a fixed result that needs to be repeated in a loop is saved in a variable outside the loop and used in the loop.

13.3 Pre-calculation

It is possible to calculate the results of a number of calculations that need to be used frequently on critical paths and save the result so that you can really use it simply to find it.

13.4 Reduced flexibility

If the target code is used in a very fixed scope, there is no need to consider too many common cases in the code, but rather to make bold assumptions about some of the specific situations that are currently known to speed up the operation.

13.5 Increase the speed of common paths.

80% of the time is spent on 20% function calls, so minimizing the time required for these 20% functions can greatly improve the performance of the entire system.

Similar examples appear in the IF (And1 && and2) and if (Or1 | | or2), if two conditions are not dependent, then the conditions that are more likely to determine the entire value of the relationship are placed before, that is, if AND1 is more likely to be false than And2, Then put AND1 in front, and if Or1 is more likely to be false than Or2, Or2 will be produced in front.

In addition to the possibility of conditional values, you can also take into account the number of instructions for each operation, so that the conditions with the smallest number of overall conditional instructions can be placed in front.

And if 5% of all external parameters are special and the other 95% are similar, then we can design a path for each of the 5% special parameters separately, thus speeding up the processing speed of the 95% common cases.

13.6 Slow-calculation

Delay the calculation until it is really needed, so that the expensive calculation is not finally used. There is nothing new in this verse.

13.7 Useless computations

There is nothing new in this verse.

13.8 architecture

When designing an object layout, consider the impact of the architecture, mainly the system cache. For example, if the length of the matrix is equal to that of the cache line, there will be frequent cache misses when the matrix is transpose. When designing two members that often need to be accessed together, it is best to allow them to be in the same cache row, which also requires starters the members to be accessed in front of them.

13.9 Memory Management

Performance is a trade. No new stuff.

13.10 Library and system calls

Many of the performance details are hidden behind libraries and system calls, so in order to learn more about these details at design time and choose the one that has just enough functionality in multiple available tools, its performance tends to be better than the more powerful version of the feature.

13.11 Compiler Optimizations

When you turn on compiler optimizations at release, you may have a lot of performance improvements.

14th. Design Optimization 14.1 Focus

Design optimizations are global and depend on other components and code.

14.2 Flexibility in design

In the early days of software development, if you do not know the hot spots of the program, then the full use of STL good. When you have some understanding of the operation of the program, you can use some flexibility to exchange performance.

14.3 cache: Timestamp

Each request in the Web service needs to be written to the log with a timestamp. If a single request requires multiple writes, the computed timestamp can be cached for use by all of these logs.

14.4 Caching: Data expansion

If an object often needs to return the value of an action, you can embed the value in the object, such as the size of the various containers.

14.5 Caching: Common code traps

If a piece of code needs to determine the type of the request each time to determine the run path, then you can split it into two pieces of code to do a single operation. This is an area where virtual functions are very good.

14.6 high-performance data structures

No new stuff.

14.7 slow calculation, useless calculation and failure codes

No new stuff.

15th. Scalability 15.1 Focus

Performance issues in parallel or concurrent environments.

15.2 SMP System

One of the performance bottlenecks of the SMP system is that multiple processors need to share the bus between the memory.

The solution is to have a large cache per processor, but the main problem is cache consistency.

The above two problems lead to the fact that the parallel performance improvement is difficult to achieve a multiple of the core number increase.

15.3 Amdahl Law

Sequential computing is a major barrier to scalability. Accelerating a segment alone will not increase the total cost of this paragraph.

15.4 Decomposition Tasks

Splitting a single task into multiple concurrent subtasks can improve the following metrics:

    1. The response time of the request.
    2. The throughput of the server.
    3. CPU usage.

I/O intensive tasks are better suited for concurrent execution.

15.5 Cache Shared Data

Example: A thread serves a request, and many of its operations are used on that request during the lifetime of the thread. One idea is to invoke pthread_getspecific at each operation, but this can lead to significant locking overhead. Another idea is to get a pointer at the beginning of a thread and pass it to all subsequent functions.

15.6 No sharing

In the above example, a better approach would be to put the thread-related stuff directly into the thread-associated structure, which would completely remove the part that needs to be serialized.

15.7 part sharing

When you do not know the number of requests, you can use a fixed-size line pool service, which has good scalability.

15.8 size of the lock

In general, it is not a good idea to fuse multiple unrelated resources under the protection of a single lock. The exception is in cases where the following two conditions are met:

    1. All shared resources are always manipulated together.
    2. There is no operation that consumes a lot of CPU time.

The size of the lock is too coarse to cause parallelism to degrade, and the granularity is too thin to increase the cost of locks and the processing time of individual tasks.

15.9 Pseudo-sharing

On SMP systems, if two locks are in the same cache line, the P1 lock operation on M1 causes the entire cache row to expire on P2, causing the P2 to reread memory when it accesses M2. The way to avoid this problem is to manually insert a certain gap between M2 and M2.

15.10 Startled birds

If you use multiple threads to accept, say 100, 100 threads will wake up when the request is made, but only 1 threads can get the request, and the other 99 threads continue to sleep. This CPU shock can cause the server to shrink and severely damage throughput. When throughput drops, the system may increase the number of threads, causing the problem to become more serious.

The workaround is to use only one thread accept and then distribute the request to other threads.

15.11 Read/write lock

No new stuff.

16th. System Architecture Relevance 16.1 focus

The things discussed in this chapter are simply listed as follows:

    1. Memory levels: From registers, L1 to L2, memory, hard drives, access delays increase in order of magnitude. The bandwidth of the registers is much larger than the L1.
    2. Correctly making a variable based on a register allows some compiler-generated individual methods to improve performance by an order of magnitude. The variable placed in the register is saved when the method is called, but eliminates the overhead of loading it into the register each time it is accessed.
    3. The data on the disk is generally stored in B + trees to reduce the number of seek.
    4. When you write code, consider the locality of the data, which can effectively reduce the frequency of the occurrence of fault pages, and greatly improve the performance.
    5. Code for the same namespace in C + + tends to belong to the same compilation unit, so the locality is better for each other. So when you organize your code, follow namespace instead of the file name.
    6. When performance is sufficient, always choose a simpler and shorter solution. Complexity is the enemy of correctness and maintainability.
    7. If more than one processor in the SMP reads or writes the same cache line, P1 writes that the other processor waits for the cache to be updated before it can be read, a wait called a cache bump.
    8. A short code sequence with a large number of jumps takes more time to execute than a long code sequence without jumps.
    9. As above, simple calculations are better than small branches.
    10. multithreaded programs, if the independence of the various threads is not good, there is a lot of shared resources, because the cost of Lock + thread context switching overhead, may result in its performance is not as single-threaded program.
    11. There are three main costs of context switching: Processor context transfer, cache and TLB loss, scheduling overhead. Where the impact of the cache may be greatest.
    12. Synchronous operations tend to be much more expensive than asynchronous operations, but asynchronous operations often require polling to get the results, and then trade-offs as needed.
    13. For programs that require minimal response latency, synchronous multithreaded scenarios are better than asynchronous polling scenarios because asynchronous scenarios lack notification means.

Reading notes: Programming techniques for improving C + + performance

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.