Ускорьте выполнение кода с помощью Pragma на C / C ++

Опубликовано: 5 Января, 2022

Основная цель компилятора - снизить стоимость компиляции и добиться от отладки ожидаемых результатов. Не все оптимизации напрямую контролируются флагом, иногда нам нужно явно объявить флаги, чтобы произвести оптимизацию. По умолчанию оптимизации отключены. Чтобы использовать подавленные оптимизации, мы будем использовать прагмы.

Пример для неоптимизированной программы: Давайте рассмотрим пример для вычисления простых чисел до 10 000 000.

Ниже приведен код без оптимизации:

// C++ program to calculate the Prime
// Numbers upto 10000000 using Sieve
// of Eratosthenes with NO optimization
#include <cmath>
#include <iostream>
#include <vector>
#define N 10000005
using namespace std;
// Boolean array for Prime Number
vector< bool > prime(N, true );
// Sieve implemented to find Prime
// Number
void sieveOfEratosthenes()
{
for ( int i = 2; i <= sqrt (N); ++i) {
if (prime[i]) {
for ( int j = i * i; j <= N; j += i) {
prime[j] = false ;
}
}
}
}
// Driver Code
int main()
{
// Intialise clock to calculate
// time required to execute without
// optimization
clock_t start, end;
// Start clock
start = clock ();
// Function call to find Prime Numbers
sieveOfEratosthenes();
// End clock
end = clock ();
// Calculate the time difference
double time_taken
= double (end - start)
/ double (CLOCKS_PER_SEC);
// Print the Calculated execution time
cout << "Execution time: " << time_taken
<< " secs" ;
return 0;
}
Выход:
Время выполнения: 0,592183 сек.

Ниже приводится оптимизация:

  1. O1: Оптимизация компиляции на O1 включает больше времени и памяти для разбивки более крупных функций. Компилятор пытается сократить как код, так и время выполнения. В O1 вряд ли какие-либо оптимизации дают отличные результаты, но O1 - это неудача для попытки улучшить оптимизацию.

    Ниже представлена реализация предыдущей программы с оптимизацией O1:

    // C++ program to calculate the Prime
    // Numbers upto 10000000 using Sieve
    // of Eratosthenes with O1 optimization
    // To see the working of controlled
    // optimization "O1"
    #pragma GCC optimize("O1")
    #include <cmath>
    #include <iostream>
    #include <vector>
    #define N 10000005
    using namespace std;
    // Boolean array for Prime Number
    vector< bool > prime(N, true );
    // Sieve implemented to find Prime
    // Number
    void sieveOfEratosthenes()
    {
    for ( int i = 2; i <= sqrt (N); ++i) {
    if (prime[i]) {
    for ( int j = i * i; j <= N; j += i) {
    prime[j] = false ;
    }
    }
    }
    }
    // Driver Code
    int main()
    {
    // Intialise clock to calculate
    // time required to execute without
    // optimization
    clock_t start, end;
    // Start clock
    start = clock ();
    // Function call to find Prime Numbers
    sieveOfEratosthenes();
    // End clock
    end = clock ();
    // Calculate the time difference
    double time_taken
    = double (end - start)
    / double (CLOCKS_PER_SEC);
    // Print the Calculated execution time
    cout << "Execution time: " << time_taken
    << " secs." ;
    return 0;
    }
    Выход:
    Время выполнения: 0,384945 сек.
    
  2. O2: Оптимизация компиляции в O2 оптимизирует в большей степени. По сравнению с O1 эта опция увеличивает как время компиляции, так и производительность сгенерированного кода. O2 включает все флаги оптимизации, указанные в O1 .

    Ниже представлена реализация предыдущей программы с оптимизацией O2:

    // C++ program to calculate the Prime
    // Numbers upto 10000000 using Sieve
    // of Eratosthenes with O2 optimization
    // To see the working of controlled
    // optimization "O2"
    #pragma GCC optimize("O2")
    #include <cmath>
    #include <iostream>
    #include <vector>
    #define N 10000005
    using namespace std;
    // Boolean array for Prime Number
    vector< bool > prime(N, true );
    // Sieve implemented to find Prime
    // Number
    void sieveOfEratosthenes()
    {
    for ( int i = 2; i <= sqrt (N); ++i) {
    if (prime[i]) {
    for ( int j = i * i; j <= N; j += i) {
    prime[j] = false ;
    }
    }
    }
    }
    // Driver Code
    int main()
    {
    // Intialise clock to calculate
    // time required to execute without
    // optimization
    clock_t start, end;
    // Start clock
    start = clock ();
    // Function call to find Prime Numbers
    sieveOfEratosthenes();
    // End clock
    end = clock ();
    // Calculate the time difference
    double time_taken
    = double (end - start)
    / double (CLOCKS_PER_SEC);
    // Print the Calculated execution time
    cout << "Execution time: " << time_taken
    << " secs." ;
    return 0;
    }
    Выход:
    Время выполнения: 0,288337 сек.
    
  3. O3: все оптимизации на уровне O2 задаются O3, и список других флагов также включен. Некоторые из флагов, которые включены в O3, - это floop-interchange -floop-unroll-jam и -fpeel-loops.

    Ниже представлена реализация предыдущей программы с оптимизацией O3:

    // C++ program to calculate the Prime
    // Numbers upto 10000000 using Sieve
    // of Eratosthenes with O3 optimization
    // To see the working of controlled
    // optimization "O3"
    #pragma GCC optimize("O3")
    #include <cmath>
    #include <iostream>
    #include <vector>
    #define N 10000005
    using namespace std;
    // Boolean array for Prime Number
    vector< bool > prime(N, true );
    // Sieve implemented to find Prime
    // Number
    void sieveOfEratosthenes()
    {
    for ( int i = 2; i <= sqrt (N); ++i) {
    if (prime[i]) {
    for ( int j = i * i; j <= N; j += i) {
    prime[j] = false ;
    }
    }
    }
    }
    // Driver Code
    int main()
    {
    // Intialise clock to calculate
    // time required to execute without
    // optimization
    clock_t start, end;
    // Start clock
    start = clock ();
    // Function call to find Prime Numbers
    sieveOfEratosthenes();
    // End clock
    end = clock ();
    // Calculate the time difference
    double time_taken
    = double (end - start)
    / double (CLOCKS_PER_SEC);
    // Print the Calculated execution time
    cout << "Execution time: " << time_taken
    << " secs." ;
    return 0;
    }
    Выход:
    Время выполнения: 0,580154 сек.
    
  4. Ос: Оптимизирован по размеру. Os включает все оптимизации O2, кроме тех, которые имеют увеличенный размер кода. Он также включает -finline-functions, заставляет компилятор настраиваться на размер кода, а не на скорость выполнения, и выполняет дальнейшую оптимизацию, предназначенную для уменьшения размера кода.

    Ниже представлена реализация предыдущей программы с оптимизацией ОС:

    // C++ program to calculate the Prime
    // Numbers upto 10000000 using Sieve
    // of Eratosthenes with Os optimization
    // To see the working of controlled
    // optimization "Os"
    #pragma GCC optimize("Os")
    #include <cmath>
    #include <iostream>
    #include <vector>
    #define N 10000005
    using namespace std;
    // Boolean array for Prime Number
    vector< bool > prime(N, true );
    // Sieve implemented to find Prime
    // Number
    void sieveOfEratosthenes()
    {
    for ( int i = 2; i <= sqrt (N); ++i) {
    if (prime[i]) {
    for ( int j = i * i; j <= N; j += i) {
    prime[j] = false ;
    }
    }
    }
    }
    // Driver Code
    int main()
    {
    // Intialise clock to calculate
    // time required to execute without
    // optimization
    clock_t start, end;
    // Start clock
    start = clock ();
    // Function call to find Prime Numbers
    sieveOfEratosthenes();
    // End clock
    end = clock ();
    // Calculate the time difference
    double time_taken
    = double (end - start)
    / double (CLOCKS_PER_SEC);
    // Print the Calculated execution time
    cout << "Execution time: " << time_taken
    << " secs." ;
    return 0;
    }
    Выход:
    Время выполнения: 0,317845 сек.
    
  5. Ofast: Ofast включает все оптимизации O3. У него также есть количество включенных флагов, которые дают супероптимизированные результаты. Ofast сочетает в себе оптимизации, произведенные на каждом из вышеперечисленных уровней О. Эта оптимизация обычно предпочитается многими конкурентоспособными программистами и поэтому рекомендуется. В случае, если объявлено более одной оптимизации, включается последняя объявленная.

    Ниже представлена реализация предыдущей программы с оптимизацией Ofast:

    // C++ program to calculate the Prime
    // Numbers upto 10000000 using Sieve
    // of Eratosthenes with Ofast optimization
    // To see the working of controlled
    // optimization "Ofast"
    #pragma GCC optimize("Ofast")
    #include <cmath>
    #include <iostream>
    #include <vector>
    #define N 10000005
    using namespace std;
    // Boolean array for Prime Number
    vector< bool > prime(N, true );
    // Sieve implemented to find Prime
    // Number
    void sieveOfEratosthenes()
    {
    for ( int i = 2; i <= sqrt (N); ++i) {
    if (prime[i]) {
    for ( int j = i * i; j <= N; j += i) {
    prime[j] = false ;
    }
    }
    }
    }
    // Driver Code
    int main()
    {
    // Intialise clock to calculate
    // time required to execute without
    // optimization
    clock_t start, end;
    // Start clock
    start = clock ();
    // Function call to find Prime Numbers
    sieveOfEratosthenes();
    // End clock
    end = clock ();
    // Calculate the time difference
    double time_taken
    = double (end - start)
    / double (CLOCKS_PER_SEC);
    // Print the Calculated execution time
    cout << "Execution time: " << time_taken
    << " secs." ;
    return 0;
    }
    Выход:
    Время выполнения: 0,303287 сек.
    

Для дальнейшей оптимизации на уровне архитектуры мы можем использовать цели с прагмами . Эти оптимизации могут дать удивительные результаты. Однако рекомендуется использовать цель с любой из оптимизаций, указанных выше.
Ниже представлена реализация предыдущей программы с Target :

// C++ program to calculate the Prime
// Numbers upto 10000000 using Sieve
// of Eratosthenes with Ofast optimization along with target optimizations
// To see the working of controlled
// optimization "Ofast"
#pragma GCC optimize("Ofast")
#pragma GCC target("avx,avx2,fma")
#include <cmath>
#include <iostream>
#include <vector>
#define N 10000005
using namespace std;
// Boolean array for Prime Number
vector< bool > prime(N, true );
// Sieve implemented to find Prime
// Number
void sieveOfEratosthenes()
{
for ( int i = 2; i <= sqrt (N); ++i) {
if (prime[i]) {
for ( int j = i * i; j <= N; j += i) {
prime[j] = false ;
}
}
}
}
// Driver Code
int main()
{
// Intialise clock to calculate
// time required to execute without
// optimization
clock_t start, end;
// Start clock
start = clock ();
// Function call to find Prime Numbers
sieveOfEratosthenes();
// End clock
end = clock ();
// Calculate the time difference
double time_taken
= double (end - start)
/ double (CLOCKS_PER_SEC);
// Print the Calculated execution time
cout << "Execution time: " << time_taken
<< " secs." ;
return 0;
}
Выход:
Время выполнения: 0,292147 сек.

Вниманию читателя! Не прекращайте учиться сейчас. Освойте все важные концепции DSA с помощью самостоятельного курса DSA по приемлемой для студентов цене и будьте готовы к работе в отрасли. Получите все важные математические концепции для соревновательного программирования с курсом Essential Maths for CP по доступной для студентов цене.

Если вы хотите посещать живые занятия с отраслевыми экспертами, пожалуйста, обращайтесь к Geeks Classes Live и Geeks Classes Live USA.