天天看點

你知道如何優化你的C語言代碼嗎?(開篇)

作者:曉亮Albert

本篇給您列舉10個C語言的優化案例,并展示未優化前和優化後的代碼。這裡我們将用到一個數組排序的例子來示範這些優化技術。

  1. 循環展開 (Loop Unrolling)

循環展開是一種将循環體重複執行多次,進而減少循環開銷的優化技術。下面是一個未優化前的例子:

void unrolled_loop(int arr[], int n) {
    for (int i = 0; i < n; i++) {
        arr[i] = i + 1;
    }
}
           

優化後的代碼:

void unrolled_loop(int arr[], int n) {
    int i = 0;
    for (; i < n - 4; i += 4) {
        arr[i] = i + 1;
        arr[i + 1] = i + 2;
        arr[i + 2] = i + 3;
        arr[i + 3] = i + 4;
    }
    for (; i < n; i++) {
        arr[i] = i + 1;
    }
}
           
  1. 資料對齊 (Data Alignment)

資料對齊是一種将資料存儲在記憶體中的位址按照一定的規則對齊,進而提高通路速度的優化技術。下面是一個未優化前的例子:

struct data {
    int x;
    int y;
};

void data_alignment(struct data* arr, int n) {
    for (int i = 0; i < n; i++) {
        arr[i].x = i;
        arr[i].y = i * 2;
    }
}
           

優化後的代碼:

struct data {
    int x;
    int y;
} __attribute__((aligned(16)));

void data_alignment(struct data* arr, int n) {
    for (int i = 0; i < n; i++) {
        arr[i].x = i;
        arr[i].y = i * 2;
    }
}
           
  1. 寄存器變量 (Register Variables)

寄存器變量是一種将變量存儲在寄存器中,進而提高通路速度的優化技術。下面是一個未優化前的例子:

void register_variable(int arr[], int n) {
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += arr[i];
    }
    printf("Sum: %d\n", sum);
}
           

優化後的代碼:

void register_variable(register int arr[], register int n) {
    register int sum = 0;
    for (register int i = 0; i < n; i++) {
        sum += arr[i];
    }
    printf("Sum: %d\n", sum);
}
           
  1. 常量傳遞 (Passing Constants)

常量傳遞是一種将常量作為參數傳遞給函數,進而提高通路速度的優化技術。下面是一個未優化前的例子:

void constant_parameter(int n) {
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        sum += i;
    }
    printf("Sum: %d\n", sum);
}
           

優化後的代碼:

void constant_parameter(const int n) {
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        sum += i;
    }
    printf("Sum: %d\n", sum);
}
           
  1. 預計算 (Precomputation)

預計算是一種在程式執行前提前計算出結果,進而減少運作時間的優化技術。下面是一個未優化前的例子:

void precomputation(int arr[], int n) {
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += arr[i];
    }
    double avg = sum / n;
    printf("Average: %.2lf\n", avg);
}
           

優化後的代碼:

void precomputation(int arr[], int n) {
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += arr[i];
    }
    double avg = (double)sum / (double)n;
    printf("Average: %.2lf\n", avg);
}
           
  1. 去除函數調用 (Eliminating Function Calls)

去除函數調用是一種将函數調用替換為函數體内的代碼,進而減少函數調用開銷的優化技術。下面是一個未優化前的例子:

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

void eliminating_function_calls(int arr[], int n) {
    int max_val = arr[0];
    for (int i = 1; i < n; i++) {
        max_val = max(max_val, arr[i]);
    }
    printf("Max value: %d\n", max_val);
}
           

優化後的代碼:

void eliminating_function_calls(int arr[], int n) {
    int max_val = arr[0];
    for (int i = 1; i < n; i++) {
        if (arr[i] > max_val) {
            max_val = arr[i];
        }
    }
    printf("Max value: %d\n", max_val);
}
           
  1. 短路求值 (Short-Circuit Evaluation)

使用短路求值可以減少不必要的運算,提高程式的執行效率。例如,在進行邏輯運算時,如果前面的表達式已經能夠确定整個表達式的值,那麼後面的表達式就不需要再進行運算了。下面是一個未優化前的例子:

bool short_circuit_evaluation(int a, int b) {
    if (a != 0 && b / a > 1) {
        return true;
    }
    return false;
}
           

優化後的代碼:

bool short_circuit_evaluation(int a, int b) {
    if (a && b > a) {
        return true;
    }
    return false;
}
           

在這個優化後的代碼中,當 a 為 0 時,第一個表達式傳回 false,後面的表達式不再進行運算,進而減少了不必要的運算。

  1. 資料結構選擇 (Choosing Data Structures)

選擇合适的資料結構可以提高程式的執行效率。例如,在需要快速查找資料時,可以使用散清單而不是數組。下面是一個未優化前的例子:

struct student {
    char name[20];
    int id;
    float gpa;
};

void choosing_data_structures(struct student arr[], int n, int id) {
    for (int i = 0; i < n; i++) {
        if (arr[i].id == id) {
            printf("Name: %s, GPA: %.2f\n", arr[i].name, arr[i].gpa);
            return;
        }
    }
    printf("Student not found\n");
}
           

優化後的代碼:

#include <stdbool.h>
#include <string.h>

#define TABLE_SIZE 1000

struct student {
    char name[20];
    int id;
    float gpa;
};

struct hash_table {
    int key;
    struct student data;
};

void choosing_data_structures(struct student arr[], int n, int id) {
    struct hash_table table[TABLE_SIZE];
    memset(table, 0, sizeof(table));

    for (int i = 0; i < n; i++) {
        int key = arr[i].id % TABLE_SIZE;
        while (table[key].key != 0) {
            key = (key + 1) % TABLE_SIZE;
        }
        table[key].key = arr[i].id;
        table[key].data = arr[i];
    }

    int key = id % TABLE_SIZE;
    while (table[key].key != 0) {
        if (table[key].key == id) {
            printf("Name: %s, GPA: %.2f\n", table[key].data.name, table[key].data.gpa);
            return;
        }
        key = (key + 1) % TABLE_SIZE;
    }

    printf("Student not found\n");
}
           
  1. 位運算 (Bitwise Operations)

使用位運算可以提高程式的執行效率,例如在進行整數倍數檢查時,可以使用位運算代替模運算。下面是一個未優化前的例子:

bool is_multiple_of_4(int n) {
    return n % 4 == 0;
}
           

優化後的代碼:

bool is_multiple_of_4(int n) {
    return (n & 3) == 0;
}
           
  1. 并行化 (Parallelization)

使用并行化可以利用多核處理器的優勢,提高程式的執行效率。下面是一個未優化前的例子:

void parallelization(float a[], float b[], float c[], int n) {
    for (int i = 0; i < n; i++) {
        c[i] = a[i] + b[i];
    }
}
           

優化後的代碼:

#include <omp.h>

void parallelization(float a[], float b[], float c[], int n) {
    #pragma omp parallel for
    for (int i = 0; i < n; i++) {
        c[i] = a[i] + b[i];
    }
}
           

繼續閱讀