2011-03-17 2 views
2

Предположим, у меня есть два целых массива a и b с 10 int s за массив. Есть ли способ добавить содержимое b[i] в a[i] с использованием трюка «memset» или «memcopy»? Я просто ищу что-то быстрее, чем очевидное для цикла w/a[i] += b[i] и т. Д.Глупый вопрос о массивах в c и/или C++

+0

Нужно ли переносить его? – EmeryBerger

+6

Кроме того, посмотрели ли вы на код сборки? Я предполагаю, что вы профилировали свой код и решили, что это имеет значение. Если нет, придерживайтесь ясности. Если это так, посмотрите на сборку - на высоких уровнях оптимизации вы можете быть удивлены тем, насколько хорош результирующий код. – EmeryBerger

+0

Существующий код прилично быстр ... однако, поскольку он предназначен для игры на мобильном устройстве, я хочу, чтобы это было так быстро, как я могу это сделать. Также мне просто любопытно в целом. Спасибо за ответы :-) – MrDatabase

ответ

3

"Глупый" - Я думаю, что это отличный вопрос!

Вы говорите, что «добавление» не «копировать» и я предполагаю, что x86:

void addintvector (int *dstp, const int *srcp, int nints) 
{ 
    int *endp; 

    endp=dst+nints; 
    nints=srcp-dstp; // reuse nints 

    while (dstp!=endp) 
    { 
    *dstp+=*(dstp+nints); // makes use of the [base+index*4] x86 addressing 
    dstp+=1; // some prefer ++dstp but I don't when it comes to pointers 
    } 
} 

Петля должна перевести в

add_label: 
    mov eax,[ebx+esi*4] 
    add [ebx],eax 
    add ebx,4 
    cmp ebx,edx 
    jne add_label 

Это пять команд в цикле: он не получит гораздо быстрее!

Это также легко клонировать для вычитания, деления и умножения вариантов.

Некоторые говорят об использовании графического процессора, но для этого требуется, чтобы: 1. графический процессор взаимодействовал с приложениями и 2. ваш массив был достаточно большим, чтобы преодолеть связанные с этим накладные расходы.

Чтобы преодолеть накладные расходы на вызов/возврат, вы можете поэкспериментировать с объявлением его встроенным.

Редактировать

Я только что прочитал комментарий «так как это для игры на мобильном устройстве», и я предполагаю, что это не платформа x86 и поэтому, вероятно, не имеет рег + рег * шкала режима адресации.Если это так, код должен быть написан

void addintvector (int *dstp, const int *srcp, int nints) 
{ 
    int *endp; 

    endp=dst+nints; 

    while (dstp!=endp) 
    { 
    *dstp+=*srcp; 
    srcp+=1; 
    dstp+=1; 
    } 
} 

Не зная, какую архитектуру вы ориентируетесь, но предполагая RISC я думаю код будет восемь инструкций долго вместо этого (в «неоптимизированном» psuedocode):

add_label: 
    mov tempreg1,[srcreg] 
    mov tempreg2,[dstreg] 
    add tempreg2,tempreg1 
    mov [dstreg],tempreg2 
    add srcreg,4 
    add dstreg,4 
    cmp dstreg,endreg 
    jne add_label 
+1

Позвольте мне сказать, что это, по крайней мере, для x86, ужасная идея. Gcc будет векторизовать простой for-loop, но в общем случае не сможет этого сделать при перезаписи для использования указателей. – EmeryBerger

+0

@EmeryBerger: Ваш комментарий неясен, так как я не могу понять, является ли это x86 вообще или комбинацией x86 и gcc. Я предполагаю, что вы имеете в виду, что в целом генерируемый код gcc будет выполняться в (эквиваленте) менее пяти инструкций для каждого добавления. Также неясно, является ли это вашим личным мнением или если это абсолютная истина. Для последнего вы должны предоставить соответствующие ссылки. –

+1

попытайтесь скомпилировать простой for-loop с -O3 для любого процессора с SSE. Посмотрите на код сборки. Вы обнаружите, что он заменил операции с добавлением и памятью широкими векторными инструкциями, которые в целом намного быстрее. См. Http://gcc.gnu.org/projects/tree-ssa/vectorization.html. – EmeryBerger

0

Не то, чтобы я знал.

Является ли очевидная петля настолько медленной, что вам действительно нужно что-то «быстрее»? Как вы могли улучшить его?

+0

Почему вы сомневаетесь в его причинах для того, чтобы задать вопрос? -1 –

2

Простой цикл добавления обычно будет достаточно быстрым, поскольку компилятор будет его векторизовать: http://gcc.gnu.org/projects/tree-ssa/vectorization.html, выводя параллельные инструкции, которые будут работать на четырех элементах массивов одновременно.

+2

Если инструкции SIMD в вашем процессоре не достаточно быстры, вы можете в векторном формате увеличить масштаб с помощью OpenCL на вашем графическом процессоре. –

2

Возможно, стоит рассмотреть OpenCL. Если у вас много векторных или матричных задач, давайте не будем решать GPU. Взгляните на образец с суммой векторов https://www.wiki.ed.ac.uk/display/ecdfwiki/OpenCL+quick+start

+2

Это должно быть достаточно большим, чтобы иметь значение. Накладные расходы связаны с отправкой данных по шине на GPU. Я сомневаюсь, что это победа на 10 очков. 1000 или более, может быть. –

+1

Согласен. Мой подход имеет смысл, например, если у одного есть много пар векторов, чтобы добавить одно и то же время. Тогда можно объединить 10-элементные векторы в большой и отправить его на GPU. –

1

Если вы хотите использовать «чистый» C, в C99 есть переменные макросы. Используйте P99 для разматывания:

#include "p99_for.h" 
#define ADDIT(Y, X, I) X[I] += Y[I] 
#define ADD_MORE(Y, X, N) P99_FOR(Y, N, P00_SEP, ADDIT, P99_DUPL(N, X)) 

линия как

ADD_MORE(A, B, 3); 

Затем расширяется

B[0] += A[0]; B[1] += A[1]; B[2] += A[2]; 
1

std::valarray кажется хорошим выбором.

#include <valarray> 
#include <algorithm> 
#include <iostream> 
#include <iterator> 

int main() 
{ 
    std::valarray<int> a(3, 10); 
    std::valarray<int> b(4, 10); 

    std::valarray<int> result = a + b; 

    std::copy(&result[0], &result[0] + result.size(), 
     std::ostream_iterator<int>(std::cout, " ")); 

    return 0; 
} 

a и b массивы с десятью элементами, 3 и 4 соответственно. Добавление двух valarray s выполняет элементное добавление. Существует множество других арифметических операций, определенных для valarray с.

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