C array = массив быстрее, чем memcpy ()

У меня есть фрагмент кода C, который я пытаюсь оптимизировать, что включает в себя установку массива a на b. В настоящее время я использую memcpy для этого, и он работает, но недостаточно быстро. Т.е.

double a[4] = {1.0, 2.0, 3.0, 4.0};
double b[4];
memcpy(b, a, sizeof(a));

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

РЕДАКТИРОВАТЬ: мне не нужно хранить массив, который можно выбросить. Мне просто нужно перейти с а на б.


person Nicholas Cooke    schedule 21.05.2013    source источник
comment
Если вы копируете данные, вы копируете данные. Я был бы удивлен, если бы при этом было легко победить memcpy.   -  person Daniel Fischer    schedule 21.05.2013
comment
объект массива оцениваются как указатели в функции memcpy. Использование указателей в memcpy не имеет значения   -  person MOHAMED    schedule 21.05.2013
comment
@Nicholas Cooke Я не могу придумать ничего лучше, чем копирование каждого элемента, если в качестве конечного результата вам понадобятся две копии данных.   -  person Kraken    schedule 21.05.2013
comment
Вы должны посмотреть на свой дизайн, чтобы увидеть, действительно ли все это копирование строго необходимо.   -  person Paul R    schedule 21.05.2013
comment
возможный дубликат Очень быстрый memcpy для обработки изображений?   -  person huon    schedule 21.05.2013
comment
Если a не требуется после копирования, но вам нужно перейти с a на b, почему бы не использовать a вместо b?   -  person jogojapan    schedule 21.05.2013
comment
Спасибо, парни! Я согласен, что в моей программе должен быть лучший способ сделать это, но я просто хотел проверить. Я где-то слышал, что если бы был указатель на массивы, я мог бы установить указатель на массив, равный указателю на массив b, и получить доступ к данным через указатели, и в этом случае данные не должны передаваться, я просто не был уверен как это сделать.   -  person Nicholas Cooke    schedule 21.05.2013
comment
@jogojapan буква в программе Я использую значения в b для определения новых значений для a. Он проходит через цикл while, проверяющий сходимость данных.   -  person Nicholas Cooke    schedule 21.05.2013
comment
@JimBalter означает ли это, что если я сделаю что-то вроде b [0] = 1, это изменится как для a, так и для b или только для b? И наоборот, если я позже изменю значение a, будет ли оно отличаться в b?   -  person Nicholas Cooke    schedule 21.05.2013
comment
Да, но я писал это до того, как вы пояснили. (Уточнение ваших потребностей очень помогает в SO.) См. Мой ответ ниже.   -  person Jim Balter    schedule 21.05.2013
comment
Николас, я полностью переработал свой ответ, наконец, я думаю, что понял, что вы пытаетесь сделать. Взгляните, может быть, это именно то, что вам нужно.   -  person Jim Balter    schedule 21.05.2013
comment
@NicholasCooke Многое зависит от того, как именно работает цикл while. Например. если он определяет новые значения a[i], для i от 0 до N, и каждое вычисление a[i] использует старые значения a[j] для j<i (но не какие-либо j>=i), тогда вы можете решить свою проблему, вычислив новые значения справа налево (т.е. для i от N до 0, считая вниз). Но это всего лишь пример. Я хочу сказать, что могут быть способы закодировать цикл while так, чтобы фактическая копия a не требовалась.   -  person jogojapan    schedule 21.05.2013
comment
@jogojapan, к сожалению, полагается на j ‹i и› = i. В этом случае это не сработает, но спасибо!   -  person Nicholas Cooke    schedule 21.05.2013
comment
@jogojapan Это умно, но на самом деле нет необходимости делать копию, независимо от того, как идут дела ... см. мой ответ.   -  person Jim Balter    schedule 21.05.2013
comment
Да, верно - а может, и нет, в зависимости от того, что именно происходит в цикле while. Он может использовать как новые, так и старые значения. В любом случае, я просто считаю важным обсудить реальный алгоритм, а не просто оптимизацию самой копии (как, конечно, вы также сказали ранее).   -  person jogojapan    schedule 21.05.2013
comment
@NicholasCooke В этом языке нет понятия скорости ... Скорость вносит ваша реализация: ваш компьютер, ваша ОС, ваш компилятор и ваша библиотека. Не путайте реализацию со спецификацией.   -  person autistic    schedule 21.05.2013
comment
@undefinedbehaviour Я спрашивал, как лучше достичь той же функциональности с помощью memcpy, но быстрее. Используя замену указателя, как это было предложено в принятом ответе, я смог ускорить свое решение. Не знаю, в чем путаница.   -  person Nicholas Cooke    schedule 22.05.2013
comment
@NicholasCooke Как вы думаете, некоторые реализации могут быстрее говорить / читать на языках, чем другие? Почему не имеет смысла придавать скорость значения языка?   -  person autistic    schedule 22.05.2013


Ответы (2)


Я использую значения в b, чтобы определить новые значения для a. Он проходит через цикл while, проверяющий сходимость данных.

В этом случае вы сможете избежать любого копирования, если переключите массивы вперед и назад, что-то вроде этого (что не соответствует тому, что вы написали; при необходимости отрегулируйте):

double array1[SIZE], array2[SIZE];
double* a = array1, double* b = array2;
generate_initial_values(array1);

for (;;)
{
    // do either 
    memcpy(b, a, sizeof array1); // sizeof either array will do; *don't* use sizeof a or b, which is only the size of the pointer, not of the array
    update_values_in_b(b);

    // or, better:
    produce_modified_values_in_b_from_a(a, b);

    if (converged(a, b)) break;
    // switch arrays
    double* temp_ptr = a;
    a = b;
    b = temp_ptr;
}

Второй способ будет быстрее, если он вам подходит. Если вам необходим memcpy, вы можете попробовать все это в Очень быстрый memcpy для обработки изображений?, но, вероятно, лучше всего использовать memcpy и установить максимально высокий уровень оптимизации компилятора. Убедитесь, что вы #include <string.h> и что аргумент размера для memcpy является константой времени компиляции (она указана выше), и посмотрите на сгенерированный код сборки, чтобы убедиться, что компилятор встраивает копию.

Изменить: подождите, вот еще одна мысль, которая даже не требует переключения массивов:

double a[SIZE], b[SIZE];
generate_initial_values(a);

for (;;)
{
    produce_modified_values_in_second_array_from_first(a, b);
    if (converged(a, b)) break;
    produce_modified_values_in_second_array_from_first(b, a);
    if (converged(b, a)) break;
}

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

void calling_function(void)
{
    ...
    double a[SIZE], b[SIZE];
    generate_initial_values(a);
    double* great_values = get_great_values(a, b); // returns either a or b
    ...
}

double* get_great_values(double* a1, double* a2)
{
    for (;;)
    {
        produce_modified_values_in_second_array_from_first(a1, a2);
        if (converged(a1, a2)) return a2;
        produce_modified_values_in_second_array_from_first(a2, a1);
        if (converged(a2, a1)) return a1;
    }
}
person Jim Balter    schedule 21.05.2013
comment
Привет, Джим, спасибо, это то, что я искал. Извините, я не объяснил это полностью с самого начала :) - person Nicholas Cooke; 21.05.2013
comment
@NicholasCooke Я сделал это даже лучше ... вам может вообще не понадобиться memcpy ... взгляните. - person Jim Balter; 21.05.2013
comment
Да, это прекрасно! Спасибо! - person Nicholas Cooke; 21.05.2013
comment
@NicholasCooke Взгляни еще раз ... Думаю, я стал совершеннее. - person Jim Balter; 21.05.2013
comment
Кстати, это классический пример проблемы XY (погуглите). Проблема была не в memcpy или указателях. - person Jim Balter; 21.05.2013

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

(Привязка к памяти означает, что memcpy() ограничена скоростью вашей оперативной памяти или контроллера памяти. Функции могут быть привязаны к ЦП, памяти, ввода-вывода и т. Д.)

На большинстве платформ memcpy() написан на настроенном вручную ассемблере и сильно оптимизирован для использования преимуществ различных функций процессора (таких как SSE). Попытка использовать несколько ядер не сработает, потому что, даже если вы распределяете работу по большему количеству ядер, вы не распределяете работу по большему количеству ОЗУ или большему количеству контроллеров памяти.

Сноски

* Некоторые платформы или наборы инструментов могут иметь плохо оптимизированный memcpy().

person Dietrich Epp    schedule 21.05.2013