2012-01-13 5 views
2

Будет ли использование глобальных переменных давать ускорение? В Руководстве разработчика программного обеспечения Intel по архитектуре (о микропроцессоре) рекомендуется использовать локальные переменные вместо глобальных. Но, рассмотрим следующий код:стек (локальный) или глобальные переменные?

void process_tcp_packets(void) { 
    char tcp_data[128]; 

    do some stuff with the tcp_data[].... 
} 

void process_udp_packets(void) { 
    char udp_data[128]; 

    do some stuff with the udp_data[] 
} 
void main_event_loop(void) { 

    while(1) { 
     if (pending_tcp_requests) { 
      process_tcp_packets(); 
     } 
     if (pending_udp_requests) { 
      process_udp_packets(); 
     } 
    } 
} 

Когда main_event_loop() выполняется, контроль потока зависит от переменных «pending_tcp_requests» и «pending_udp_requests». Обе функции process_tcp_packets() и process_udp_packets() при вызове будут выделять локальные переменные в указателе текущего стека. Это означает, что если код постоянно переключает обе функции, локальные переменные будут распределены по одному и тому же адресу памяти. Совместное использование адреса памяти между обеими функциями будет вытеснять данные из текущего кеша L1 и медленного выполнения. Итак, используя глобальные переменные вместо локальных, мы можем ускорить выполнение. Правильно это или нет?

Если да, есть ли недостаток для использования глобальной переменной в этом случае?

+2

Зачем передавать данные памяти из базы данных из кэша? –

+0

«если код постоянно переключает обе функции, локальные переменные будут распределены по одному и тому же адресу памяти» <- hmmm, я не уверен в этом ... Конечно, это не будет через два исполнения программы на современных системах с рандомизацией адресного пространства, но даже в одном и том же контексте выполнения я не знаю, правда ли это. – fge

+6

Вы, кажется, используете сеть. Озабоченность по поводу адресов памяти совершенно незначителен по сравнению с производительностью сети. –

ответ

3

Глобальные переменные вряд ли принесут вам пользу по стеку в этом случае.

  1. «Выделение» памяти в стеке просто добавляет число к указателю стека, & frac12; цикл.
  2. В 64 КБ кеш L1 достаточно большой, чтобы содержать много кадров стека. Более того, последние несколько кадров стека (потому что вы нажимаете параметры при вызове функции) почти всегда в кеше. Фактически, область для следующего кадра, который будет выделен, обычно находится в L1, потому что вы часто просто выходили из другой функции, которая использовала его.
  3. Напротив, глобальные переменные быстро выпадают из L1. Помните, что стек затрагивается в начале и в конце каждой функции (просто для того, чтобы нажать обратный адрес), а process_tcp_packets() и process_udp_packets() будут использовать примерно одинаковое адресное пространство для хранения своих локалей.
  4. Совместное использование адресов памяти между функциями не выдает данные из кеша. Напротив, это заставляет вас еще, вероятно, попадет в кеш. The more recently you have touched an address, the more likely it is to be in cache.
+0

1. но распределение глобальных переменных стоило 0 циклов. он представляется как фиксированный адрес. – Nulik

+0

2. 64kb является небольшим, если у вас много запросов, тем больше у вас свободного кеша L1, тем лучше – Nulik

+0

3. Это может быть неверно, чем больше переменная доступна, тем менее вероятно, что она будет выведена – Nulik

1

Лучшее, что нужно сделать, это измерить. Обратите внимание, однако, что массивы семантически всегда новы, хотя они будут содержать предыдущие байты. Если вы намереваетесь сохранить некоторое состояние в этих массивах, вам нужно будет сделать их static, и в этом случае они также получат разные области памяти. Я не измерил, но я ожидал бы, что использование локальной переменной быстрее глобальных переменных, потому что никогда не нужно писать их в память, если переполнение кэшей не происходит.

1

Проблема, которую вы описываете, может быть исправлена ​​без использования глобальной переменной. Сделайте локальную переменную в стеке выше и передайте ссылку (указатель) на этот метод. Это лучшее из обоих миров.

void process_tcp_packets(char** tcp_data) { 
    do some stuff with the tcp_data[].... 
} 

void process_udp_packets(char** udp_data) { 
    do some stuff with the udp_data[] 
} 

void main_event_loop(void) { 
    char udp_data[128]; 
    char tcp_data[128]; 
    while(1) { 
     if (pending_tcp_requests) { 
      process_tcp_packets(&tcp_data); 
     } 
     if (pending_udp_requests) { 
      process_udp_packets(&udp_data); 
     } 
    } 
} 

Глобальные переменные следует избегать по нескольким причинам. В конце концов становится трудно понять, почему данные изменяются в глобальном из-за слишком большого количества мест, где глобальный доступ может быть доступен (т. Е. Вся программа). Используя локальные переменные, переменная не может быть доступна извне блока исполнения, если явно не передана. Это позволяет разделить вашу программу на несколько методов, которые могут получить доступ к определенной части памяти, значительно упрощая отладку и возможность поддерживать программу (со временем).

+2

Помимо дополнительной разыменования указателя, которая требуется ... –

0

В середине функции компромисса с тем, что вы предлагаете, объявляются эти переменные массива как функция static. Они «действуют» как глобальные переменные, поскольку все вызовы функции повторно используют одну и ту же ячейку памяти для массива, но у вас нет ни одной проблемы с областью глобальных переменных. Массивы могут получить только с.

void process_tcp_packets(void) { 
    static char tcp_data[128]; 

    do some stuff with the tcp_data[].... 
} 

void process_udp_packets(void) { 
    static char udp_data[128]; 

    do some stuff with the udp_data[] 
} 
+0

Вы действительно уничтожаете реинтрастность точно так же, как и глобальные переменные. –

+0

Да, несколько потоков, вызывающих одну и ту же функцию, могут разрушить хаос. –

+0

Действительно. Так будет и рекурсивная функция (хотя 'process_tcp_packets' не звучит как рекурсивная функция!). –

4

Разделение адреса памяти между двумя функциями будут выселять данные из текущего кэша L1 и медленного выполнения. Таким образом, используя глобальные переменные вместо локальных, мы можем ускорить выполнение. Правильно это или нет?

Непонятная посылка для однопоточного использования памяти.

Совместное использование одной и той же памяти между потоками с использованием разных кэшей ЦП приведет к конфликту с кешем (и, как правило, с промывкой). Совместное использование памяти между функциями в одном потоке с использованием одного и того же кэша ЦП не будет делать этого вообще.

Кроме того, на некоторых платформах локальные переменные требуют меньше инструкций для доступа, чем глобальные переменные (поскольку они являются смещениями локального указателя стека и обычно могут быть закодированы в < = 2 байта, где глобальные переменные могут отсутствовать).

+0

+1 для исправления помещения. –

+0

Не говоря уже о том, что глобальные переменные ухудшают оптимизацию, потому что компилятор должен предположить, что вызовы функций (или другие потоки) могут получить доступ (amd/или изменить) переменные – Grizzly

+0

@ Grizzly, да, ну, я подумал о метании в семантике MSVC для барьеры компилятора по отношению к глобальным переменным не помогли бы :) – MSN

Смежные вопросы