C++Builder 5 Simple Ways To Optimize C++ Arithmetic Operations

FireWind

Свой
Регистрация
2 Дек 2005
Сообщения
1,957
Реакции
1,203
Credits
4,034
5 Simple Ways To Optimize C++ Arithmetic Operations
By Yilmaz Yoru September 27, 2021

What is optimization in arithmetic operations? How can I optimize my equations or functions? Which arithmetic operation is faster than another one? Where should I optimize?

Optimization is one of the important part of programming but we should note that it is not as important in C++ in most cases for modern CPUs and GPUs today. Do not spend too much time to optimize if it is not needed. In this post, we will explain where you should optimize and 5 ways to optimize C++ arithmetic operations.

What is Optimization ?​

Для просмотра ссылки Войди или Зарегистрируйся is really important in programming. Mathematical Optimization also called Mathematical Programming is the selection of the best mathematical elements or functions about some criterion, from a set of available alternatives. In programming, optimization problems of all sorts arise in all quantitative disciplines from computer science and engineering to operations research and economics, and the development of optimal solutions to problems has been of interest in mathematics for centuries. Sometimes it’s not enough to just get an answer – sometimes it’s desirable to get the best and most efficient answer.

Where and how can I optimize in programming ?​

Today we have very high-speed computers and many new mathematical and logical instruction sets in our CPUs and GPUs. In general, if you have single operations, in this modern world of powerful compilers running on advanced modern hardware it’s not as critical as it used to be to spend excessive time optimizing your applications. Don’t forget that efficient coding takes time. Optimization may take at least twice your coding time. For example, most accounting applications don’t require optimizations when doing sum add calculating taxes and other expenses. Most of the operations are done by a single click in a single method or function which ends less than in milliseconds. These kinds of applications may not require much optimization, if any.

If you have a lot of datapoints – pixels, videos, data – to handle with heavy iterating, repeating computations, heavy loops or do-whiles(), recursive functions then you should focus on optimization in those areas instead. This will make your process faster and more robust for future extensions.

1. Simplify your equations​

Try to simplify your equations. There are simplification methods in some of the mathematical applications. You can use equation editors like Mathlab, Mathematica, Для просмотра ссылки Войди или Зарегистрируйся, Mapple etc. At least try to simplify your equations on paper.

Sometimes terms in math can be faster than you expected. for example division by 2 is faster than multiplication by 0.5. In many equations, these kinds of terms are canceled out.

Note that the compiler cannot find these simplifications, but you can. Eliminating a few expensive operations inside an inner loop can speed your program more than days working on other parts.

2. Focus on Modern CPU and GPU Architectures​

Optimization methods are also evolving because of many advances in CPU and GPU architectures. Optimization for CPU architectures requires a good assembler programming language. You should know well about how C++ transforms your codes to these machine codes. This will help you in understanding optimization in that architecture.

On modern CPUs and GPUs, floating-point operations have almost the same throughput as integer operations. In compute-intensive programs like ray tracing. This leads to a negligible difference between integer and floating-point costs. This means you should not go out of your way to use integer operations.

The difference between math on integers, fixed points, 32-bit floats, and 64-bit doubles is not as big as you might think. Double-precision floating-point operations may not be slower than single-precision floats, particularly on 64-bit machines. Modern CPUs and GPUs are modernized for the 64bits and using double precision, which means there is no speed change between floats and doubles. Thus, in most cases, ray tracers run faster using all doubles than all floats on the same machine.

3. You should know your compiler options​

Another thing is you should know well about optimization options -O0, -O1 -O2, and other necessary options required for the new microchip architectures. These options may improve the performance of your methods or functions.

Also, note that a debug version is also noticeably slower than a release version. Thus your final tests should be on the release version.

4. Use operators wisely​

Operators are the important part. You should think of operators with a focused CPU/GPU architecture. In general,
  • For basic data types, use the operators + , – , * , and / instead of the operators += , -= , *= , and /=
  • For the most classes, use the operators += , -= , *= , and /= , instead of the operators + , – , * , and /
  • If there is integer multiplication and division and there is a possibility to use bit shifting operations such as >> and <<, use bit shifting.

5. Test and improve your code speed​

The best way to understand which method or function is faster is to test this code and measure the duration elapsed. You can use SetPriorityClass() method to set priority to REALTIME_PRIORITY_CLASS.

For example, this VCL example below (with a Memo and Button) can be used to test the speed of your methods.
C++:
//---------------------------------------------------------------------------
 
#include <vcl.h>
#include <chrono>
 
#pragma hdrstop
 
#include "Optimization_Arithmetics_Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
 
#define MAXIT  10000000000
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)  : TForm(Owner)
{
 
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  UInt64 i;
  double x = M_PI, y = M_PI, z;
  std::chrono::duration<double>  diff0, diff;
 
  SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
 
  //--------------------------------------------
  auto start = std::chrono::steady_clock::now();
  for(  i = 0; i<MAXIT; i++ )
  {
       // no operation here
  }
  auto end = std::chrono::steady_clock::now();
  diff0 = end-start;
  Memo1->Lines->Add("No Operation Loop:"+ FloatToStr(diff0.count()) );
 
  //--------------------------------------------
  start = std::chrono::steady_clock::now();
  for(  i = 0; i<MAXIT; i++ )
  {
 // Add your function or method here
  }
  end = std::chrono::steady_clock::now();
  diff = end-start;
  Memo1->Lines->Add( L"Diff: " + FloatToStr(diff.count() - diff0.count() ) );
}
//---------------------------------------------------------------------------