2016-09-14 2 views
4

Предположим, например, что я хотел, чтобы инициализировать свои переменные с помощью функции:Precalculated определение переменной против инициализируется путем вычисления

int x[10]; 
void init_x(){ 
    for(int i=0; i<10; ++i){ 
     x[i]=i*i; 
    } 
} 

Он не должен быть точно такую ​​функцию, она может быть более сложным и сделать на большем массиве или другом типе int, но в любом случае точка в том, что результат является детерминированным. Мой вопрос: было бы лучше (например, будет ли моя программа инициализироваться быстрее каждый раз), чтобы просто вычислить результат этого заранее и просто определить его прямо?

int x[10]={0, 1, 4, 9, etc...} 

Таким образом, я просто запустить функцию инициализации один раз (например, запустить функцию, а затем скопировать + вставить результаты в определении массива и комментарии код из), а не снова и снова каждый раз, когда я открываю программу , (По крайней мере, так я и предполагаю)

Есть ли недостатки для этого?

+3

Если массив мал, время выполнения будет немного отличаться один так или иначе. Но если позже вы захотите изменить размер массива или его значения, код будет более гибким, если массив будет вычисляться во время выполнения, а вам придется перепечатать, возможно, большое количество значений, с риском опечаток. Я бы сказал, что это ответ, но для вашего представления о том, что значит «лучше». Альтернативой может быть наличие служебной программы, которая генерирует для вас исходный код. –

ответ

3

Если я правильно понял ваш вопрос, вы спрашиваете, можете ли вы выполнять вычисления во время компиляции, а не во время выполнения, и если есть оговорки?

Ответ зависит от сложности расчетов. Если они простые (детерминированные, как вы говорите), вы можете сделать это с успехом. Оговорки заключаются в том, что код для выполнения вычислений может быть менее простым для чтения, и он может значительно увеличить время компиляции.

Обобщение этого метода называется meta-programming, где вы добавляете еще один уровень кода-преобразования (компиляции) перед обычным кодом -> двоичное преобразование.

Вы можете использовать ограниченные формы, используя предварительный процессор. GCC также поддерживает некоторые выражения, которые оцениваются статически. Другие методы включают использование X-Macros в основном для получения параметрических шаблонов, например, в C++.

Существуют библиотеки, которые могут выполнять вычисления Turing-complete во время компиляции с использованием предварительного процессора (P99 for instance). Обычно синтаксис волосатый с большим количеством условностей и многими идиомами, чтобы учиться, прежде чем быть продуктивным.

В отличие от сложного метапрограммирования, я добился большей четкости и понимания кода со стороны коллег, поддерживающих мой код, при генерации кода с использованием, например, сценарий Perl или Python, чем когда я взломал что-то вместе с предварительным процессором.

EDIT:

Чтобы ответить на ваш вопрос с примером, я вам скажу, что я пишу много C-коду профессионально для микроконтроллеров с 4-16kb RAM и флэш-16-128kb кодового пространством. Большинство приложений работают как минимум на десять лет и потребуют обновления и добавления функций. Это означает, что я должен заботиться о том, чтобы не тратить ресурсы, поэтому я всегда буду предпочитать, что что-то можно вычислить во время компиляции, а не во время выполнения. Это экономит пространство кода за счет дополнительной сложности в сборке. Если данные постоянны, это также означает, что я могу поместить его в флеш-память только для чтения и сэкономить драгоценное ОЗУ.

Другой пример - в aes-min project, который представляет собой небольшую реализацию AES128. Я думаю, что есть выбор сборки, так что компонент в алгоритме (S-box?) Предварительно вычисляется и помещается в ПЗУ вместо ОЗУ. Другие симметричные алгоритмы шифрования должны вычислять некоторые данные из ключа, и если ключ является статическим, эта техника предварительного расчета может быть использована эффективно.

1

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

0

Мой вопрос: было бы лучше (например, будет ли моя программа инициализироваться быстрее каждый раз), чтобы просто вычислить результат этого заранее и просто определить его прямо?

Это будет быстрее. Будет ли это достаточно значительным для вас, зависит от того, насколько сложны вычисления и как часто вам нужно инициализировать переменные.

Если вы решили инициализировать массив жестко закодированными номерами, я бы настоятельно рекомендовал добавлять комментарии или код в комментарии, которые объясняют, как вы получили номера в первую очередь. В противном случае, это будет головная боль обслуживания.

Есть ли недостатки для этого?

Я бы не стал жестко кодировать цифры, если нет подтверждающих доказательств, которые демонстрируют значительную экономию, делая это.

+2

Является ли чтение 'int' с диска действительно быстрее, чем вычисление и хранение его CPU? (Обратите внимание, что речь идет о системах с массовым хранением, а не с встроенными микроконтроллерами, где данные и код уже готовы к использованию во Flash уже.) – Olaf

+0

Р. Саху, аргумент «быстрее» обсуждается в ответе пользователя3386109: он может быть не быстрее , –

+1

@Olaf, когда загружается исполняемый файл/dll, данные также считываются одновременно. Я не знаю о платформах, где данные загружаются независимо. Может быть, есть. –

2

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

2

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

Вот пример того, как это можно сделать:

genx.c (программа, которая precalcuates массив):

#include <stdio.h> 

int main() 
{ 
    int i; 
    printf("int x[] = {"); 
    for(i=0; i<10; ++i){ 
     if (i) printf(","); 
     printf(" %d", i*i); 
    } 
    printf(" };\n"); 
    return 0; 
} 

При запуске эти выходы:

int x[] = { 0, 1, 4, 9, 16, 25, 36, 49, 64, 81 }; 

Файл:

CFLAGS=-Wall -Wextra 

app: app.c x.c 
     gcc $(CFLAGS) -o app app.c 

x.c: genx 
     ./genx > x.c 

genx: genx.c 
     gcc $(CFLAGS) -o genx genx.c 

clean: 
     rm -f app genx x.c 

app.c (файл приложения):

#include <stdio.h> 

#include "x.c" 

int main() 
{ 
    int i; 
    for (i=0;i<10;i++) { 
     printf("x[%d]=%d\n",i,x[i]); 
    } 
    return 0; 
} 

При запуске make app, он видит, что x.c является зависимость и первым запускает цель для этого. Цель x.c построена путем запуска genx, который сам скомпилирован цели genx.

Предполагая, что genx.c не изменяется, x.c устанавливается один раз и его содержимое включается там, где это необходимо.

app Выход:

x[0]=0 
x[1]=1 
x[2]=4 
x[3]=9 
x[4]=16 
x[5]=25 
x[6]=36 
x[7]=49 
x[8]=64 
x[9]=81 
Смежные вопросы