Алгоритм: вынимаем каждый 4-й элемент массива

0

У меня есть два огромных массива (int source [1000], dest [1000] в коде ниже, но в действительности есть миллионы элементов). Исходный массив содержит ряд int из которых я хочу скопировать 3 из каждых 4.

Например, если исходный массив:

int source[1000] = {1,2,3,4,5,6,7,8....};
int dest[1000];

Вот мой код:

for (int count_small = 0, count_large = 0; count_large < 1000; count_small += 3, count_large +=4)
    {
      dest[count_small] = source[count_large];
      dest[count_small+1] = source[count_large+1];
      dest[count_small+2] = source[count_large+2];
    }

В конце концов, выход консоли console будет:

1 2 3 5 6 7 9 10 11...

Но этот алгоритм настолько медленный! Есть ли алгоритм или функция с открытым исходным кодом, которую я могу использовать/включить?

Спасибо :)

Изменение: фактическая длина моего массива составит около 1 миллиона (640 * 480 * 3)

Редактирование 2: обработка этого цикла занимает от 0,98 секунды до 2,28 секунды, а другой код занимает от 0,08 секунды до 0,14 секунды, поэтому устройство использует не менее 90% времени процессора только для цикла

  • 0
    Это вопрос C или C #, Гунтрам? Ваши теги говорят C, но я не был уверен, было ли это ошибкой
  • 0
    Учитывая определения массива, это определенно не C #.
Показать ещё 17 комментариев
Теги:
arrays
algorithm
real-time

5 ответов

3

Вы можете попробовать memcpy вместо отдельных назначений:

memcpy(&dest[count_small], &source[count_large], sizeof(int) * 3);
  • 0
    Это не поможет Компиляторы умны, поэтому такие оптимизации будут выполняться автоматически. И даже если нет. Это не сделает такой медленный алгоритм быстрым.
  • 0
    Это вызов функции; Не уверен, что это повысит производительность, особенно если вы включите оптимизацию компилятора для развертывания цикла!
Показать ещё 7 комментариев
3

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

С учетом сказанного, однако, время обработки 1000 элементов (Edit: или один миллион элементов) будет совершенно тривиальным. Если вы считаете, что это узкое место в вашей программе, вы ошибаетесь.

  • 0
    Спасибо, я приму это во внимание!
3

Прежде чем делать гораздо больше, попробуйте профилировать свое приложение и определить, является ли это лучшим местом для проведения вашего времени. Затем, если это горячее пятно, определите, насколько это быстро, и как быстро вам это нужно/может достичь? Затем проверьте альтернативы; накладные расходы на потоки или OpenMP могут даже замедлить работу (особенно, как вы уже отмечали, если вы работаете на одном основном процессоре, и в этом случае это вообще не поможет). Для одиночной резьбы я бы посмотрел на memcpy в соответствии с ответом Шона.

@Sneftel также ссылается на другие варианты ниже, содержащие SIMD-целые числа.

Один из вариантов - попытаться выполнить параллельную обработку цикла и посмотреть, поможет ли это. Вы можете попробовать использовать стандарт OpenMP (см. Ссылку Википедии здесь), но вам придется попробовать его для вашей конкретной ситуации и посмотреть, помогло ли это. Я использовал это недавно в реализации ИИ, и это очень помогло нам.

#pragma omp parallel for
for (...)
{
   ... do work
}

Помимо этого, вы ограничены собственными оптимизациями компилятора.

Вы также можете посмотреть недавнюю поддержку потоков в C11, хотя вам может быть лучше использовать предварительно реализованные средства инфраструктуры, такие как parallel_for (доступны в новой среде выполнения Windows Concurrency Runtime через PPL в Visual Studio, если это то, что вы используете), чем сворачивая свои собственные.

parallel_for(0, max_iterations,
    [...] (int i)
    {
        ... do stuff
    }
);

Внутри цикла for вас все еще есть другие варианты. Вы можете попробовать цикл for, который выполняет итерацию и пропускает все, вместо того, чтобы делать 3 копии на итерацию (просто пропустите, когда (i+1) % 4 == 0), или выполните операции блочной memcopy для групп из 3 целых чисел в соответствии с ответом Seans, Для некоторых из них вы можете добиться немного разных оптимизаций компилятора, но это маловероятно (memcpy, вероятно, так же быстро, как вы получите).

for (int i = 0, int j = 0; i < 1000; i++)
{
  if ((i+1) % 4 != 0)
  { 
    dest[j] = source[i];
    j++;
  }
}

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

  • 1
    ИМХО, действительно маловероятно, что OpenMP даст здесь какую-то выгоду, и, как вы говорите, накладные расходы вполне могут сделать его значительно медленнее. Текущий код уже будет насыщать пропускную способность памяти, и если он не очень осторожен с распределением и сегментированием, использование нескольких потоков приведет к ложным штрафам за совместное использование.
  • 0
    Согласовано - но если (и только если) это определенное узкое место, которое требует адресации для повышения производительности, то стоило бы быстро опробовать различные варианты и провести их тестирование
Показать ещё 4 комментария
0

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

0

Является ли ваш массив размером всего 1000? Если да, то как это медленно? Это должно быть сделано в кратчайшие сроки! Пока вы создаете новый массив и однопоточное приложение, это единственный удаленный AFAIK.

Однако, если массивы данных огромны, вы можете попробовать многопоточное приложение.

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню