Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
1 / 1 / 0
Регистрация: 30.03.2017
Сообщений: 141

Быстрая функция копирования блоков памяти memcpy с использованием AVX инструкций

01.02.2024, 22:42. Показов 4646. Ответов 31
Метки avx, с++ (Все метки)

Студворк — интернет-сервис помощи студентам
Доброго времени суток есть кастомная реализация функции memcpy, которая копирует блоки памяти выровненные по 32, размером кратному 32. Вопрос, что делать если при копировании блоки не кратны 32? Я планирую использовать для копирования строк поэтому была мысль хвост добивать нулями. Но хотелось бы реализовать идею дабивки хвоста побайтно, как это сделать?

Функция копирования памяти memcpy с использованием регистров __m256

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <immintrin.h>
void avx_memcpy(void *  pvDest, void *pvSrc, size_t nBytes) {
  assert(nBytes % 32 == 0);
  assert((intptr_t(pvDest) & 31) == 0);
  assert((intptr_t(pvSrc) & 31) == 0);
  const __m256i *pSrc = reinterpret_cast<const __m256i*>(pvSrc);
  __m256i *pDest = reinterpret_cast<__m256i*>(pvDest);
  int64_t nVects = nBytes / sizeof(*pSrc);
  for (; nVects > 0; nVects--, pSrc++, pDest++) {
    const __m256i loaded = _mm256_stream_load_si256(pSrc);
    _mm256_stream_si256(pDest, loaded);
  }
  _mm_sfence();
}
Тестирование копирования:
C++
1
2
3
4
5
6
7
8
9
    wchar_t fm1 alignas(32)[1024]{L"01234"};
    wchar_t fm2 alignas(32)[] = L"Тест быстрого копирования строки с помощью функции avx_memcpy, с использованием avx инструкций!";
    
    std::wcout << fm2 << L" L: " << (sizeof(fm2) /sizeof(wchar_t)) << L" %32: " << ((sizeof(fm2) /sizeof(wchar_t))   % 32) << std::endl;
    
    avx_memcpy(fm1, fm2, (sizeof(fm2)) );
        std::wcout << fm1  << std::endl;
        _wsystem(L"pause");
        exit(0);
вывод на консоль:
Тест быстрого копирования строки с помощью функции avx_memcpy, с использованием avx инструкций! L: 96 %32: 0
Тест быстрого копирования строки с помощью функции avx_memcpy, с использованием avx инструкций!
Для продолжения нажмите любую клавишу . . .
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
01.02.2024, 22:42
Ответы с готовыми решениями:

Отключение инструкций avx 512 на intel 11 поколения
Здравствуйте! Кто разбирался с отключением инструкций avx 512 в процессорах intel 11 поколения? Речь идёт о ноутбуках clevo с...

Расширение ядра Windows XP mod 2021 c добавлением AVX инструкций
Недавно появилась модификация ядра Windows XP со включением AVX/AVX2 инструкций процессора. Теперь Виндовс ХП ещё ближе к Виндовс 10... А...

Как избавиться от побайтового копирования? memcpy
Здравствуйте, недавно использовал код , который побайтово копирует в кольцевой буфер данные static quint16 const BUF_SIZE = 4096; ...

31
 Аватар для vlisp
1071 / 992 / 153
Регистрация: 10.08.2015
Сообщений: 5,447
23.04.2026, 10:18
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Signum7 Посмотреть сообщение
вы не понимаете как под капотом работает с++!
с++ это язык, он под капотом никак не работает, у него нет капота, как у урала
Цитата Сообщение от Signum7 Посмотреть сообщение
все высокоуровневые надстройки, синтаксический сахар, используют низкоуровневые (сишные как вы говорите) методы!
вот именно, я не хочу понимать как это устроенно на низком уровне. я даже не хочу понимать, что память копируется. я хочу сосредоточиться на объектной модели - в этом суть плюсов. А все вот это вот я доверяю оптимизации компилятора.
ты написал некую функцию. но ты не сделал сравнительный анализ быстродействия. ты думаешь твой код будет кому-то интересен если в нем нет даже комментариев?
0
1 / 1 / 0
Регистрация: 30.03.2017
Сообщений: 141
23.04.2026, 10:30  [ТС]
Цитата Сообщение от vlisp Посмотреть сообщение
вот именно, я не хочу понимать как это устроенно на низком уровне. я даже не хочу понимать, что память копируется. я хочу сосредоточиться на объектной модели - в этом суть плюсов. А все вот это вот я доверяю оптимизации компилятора.
ты написал некую функцию. но ты не сделал сравнительный анализ быстродействия. ты думаешь твой код будет кому-то интересен если в нем нет даже комментариев?
Позиция "я не хочу понимать" — это путь в юзеры, а не в системные разработчики. С++ всегда был про контроль над каждым байтом и тактом, а не про слепую веру в "умный компилятор". Если ты отгораживаешься от железа своей объектной моделью, не удивляйся, что реальность потом бьет по производительности. Мы и так погрязли в софтовом ожирении, когда сраные текстовые редакторы тормозят на восьмиядерниках. Нормальные либы пишут те, кто понимает, как данные ходят по шине, а не фантазеры. Кому лень вникать — реально, в коридоре налево Питон уже заждался..
0
23.04.2026, 10:32

Не по теме:

Цитата Сообщение от Signum7 Посмотреть сообщение
вы не понимаете как под капотом работает с++!
Цитата Сообщение от vlisp Посмотреть сообщение
с++ это язык, он под капотом никак не работает, у него нет капота, как у урала
вам ещё многому придётся научиться, юноши,
капот не у урала,
капот - это женское платье.

0
Заблокирован
Эксперт функциональных языков программированияЭксперт С++
23.04.2026, 13:03
Цитата Сообщение от volodin661 Посмотреть сообщение
вам ещё многому придётся научиться, юноши,
капот не у урала,
капот - это женское платье.
так а с какой стати юношам нужно разбираться в женских платьях?
0
 Аватар для vlisp
1071 / 992 / 153
Регистрация: 10.08.2015
Сообщений: 5,447
23.04.2026, 13:56
Цитата Сообщение от Signum7 Посмотреть сообщение
Позиция "я не хочу понимать" — это путь в юзеры, а не в системные разработчики.
Это типичное заблуждение дилетанта-велосипедиста.

Добавлено через 24 минуты
Цитата Сообщение от Signum7 Посмотреть сообщение
весь с++ построен на си внутри если спустится вглубь!
Это так и не так
не собирается твоя шляпа
0
Заблокирован
23.04.2026, 14:37
"Я буду долго гнуть велосипед!"
Миниатюры
Быстрая функция копирования блоков памяти memcpy  с использованием AVX инструкций  
0
1 / 1 / 0
Регистрация: 30.03.2017
Сообщений: 141
23.04.2026, 15:41  [ТС]
Данную тему я открыл два года назад! и ещё тогда выяснил что стандартный memcpy хорошо оптимизируется особенно на маленьких данных которые хорошо ложатся в чистый L1/L2! этого достаточно в 99% случаев и так как у меня был чисто спортивный интерес я забил. Но совсем другое дело когда нужно непрерывным потоком гнать данные из ОЗУ через PCI шину в ВОЗУ. тут стриминг в плюсе! код использую в реальной разработке пример CPU -> GPU upload:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
    [[nodiscard]] bool upload_texture_data(ID3D11Texture2D* target, D3D11_MAP map_type, const void* src, int w, int h, size_t src_pitch_bytes, size_t bpp) noexcept
    {
        assert(target != nullptr && "upload_texture_data: target texture is null");
        assert(src != nullptr && "upload_texture_data: src data is null");
        assert(w > 0 && h > 0 && "upload_texture_data: invalid dimensions");
        D3D11_MAPPED_SUBRESOURCE ms{};
        HRESULT hr = ctx->Map(target, 0, map_type, 0, &ms);
        if (FAILED(hr)) [[unlikely]]
        {
            warn_hr("Map", hr);
            handle_device_lost(hr);
            return false;
        }
 
        const size_t row_bytes = static_cast<size_t>(w) * bpp;
        const size_t src_row_bytes = src_pitch_bytes;
 
        assert(src_row_bytes >= row_bytes);
        assert(src_row_bytes % 32 == 0);
        COM_ASSUME(src_row_bytes % 32 == 0); // C++23 AVX2 hint: caller гарантирует выравнивание
 
        const uint8_t* s = reinterpret_cast<const uint8_t*>(src);
        uint8_t* d = reinterpret_cast<uint8_t*>(ms.pData);
 
        const ptrdiff_t gpu_pitch = static_cast<ptrdiff_t>(ms.RowPitch);
        const ptrdiff_t src_pitch = static_cast<ptrdiff_t>(src_row_bytes);
        const uint32_t rows = static_cast<uint32_t>(h);
 
        if (ms.RowPitch == row_bytes && src_row_bytes == row_bytes) [[likely]]
        {
            memcpy_stream_avx2<false, true>(d, s, row_bytes * rows);
        }
        else
        {
            for (uint32_t y = 0; y < rows; ++y)
            {
                memcpy_stream_avx2<false, false>(d, s, row_bytes);
                s += src_pitch;
                d += gpu_pitch;
            }
            memcpy_stream_avx2_fence();
        }
 
        ctx->Unmap(target, 0);
        return true;
    }
и сам стример:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// Вызывать один раз после всей пачки NT-записей, не внутри цикла.
FAST_FORCEINLINE void memcpy_stream_avx2_fence() noexcept { _mm_sfence(); }
 
// Safe=true — рантайм-проверки на null/размер; Fence=true — sfence перед выходом.
template <bool Safe = false, bool Fence = true>
FAST_FORCEINLINE void memcpy_stream_avx2(void* __restrict dstv, const void* __restrict srcv, size_t bytes) noexcept
{
    assert(dstv != nullptr && "memcpy_stream_avx2: dst is null");
    assert(srcv != nullptr && "memcpy_stream_avx2: src is null");
#ifndef NDEBUG
    const uintptr_t d_ptr = reinterpret_cast<uintptr_t>(dstv);
    const uintptr_t s_ptr = reinterpret_cast<uintptr_t>(srcv);
    assert((d_ptr >= s_ptr + bytes || s_ptr >= d_ptr + bytes) && "memcpy_stream_avx2: memory regions overlap! Undefined Behavior.");
#endif
 
    if constexpr (Safe)
    {
        if (bytes == 0 || !dstv || !srcv) [[unlikely]] return;
    } else {
        COM_ASSUME(dstv != nullptr);
        COM_ASSUME(srcv != nullptr);
    }
    auto*       dst = static_cast<uint8_t*>(dstv);
    auto* const src = static_cast<const uint8_t*>(srcv);
 
    // < 1 KB — через L1 дешевле NT-stores
    if (bytes < 1024) [[likely]]
    {
        std::memcpy(dst, src, bytes);
        // [ВАЖНО] sfence здесь НЕ ошибка и строго обязателен!
        // Целевая память (dst) при загрузке на GPU (D3D11_MAP) помечена как Write-Combining (WC).
        // При записи в WC-память даже обычные инструкции (используемые внутри std::memcpy)
        // игнорируют кэш L1/L2 и оседают в скрытых Write-Combining буферах (WCB) процессора.
        // Если не сделать принудительный сброс (flush) через sfence, вызов D3D11 Unmap 
        // может произойти до того, как данные физически улетят по шине PCIe в VRAM.
        // Результат отсутствия sfence: разорванные текстуры и мерцающие артефакты.
        if constexpr (Fence) { _mm_sfence(); }
        return;
    }
 
    constexpr size_t A    = 32;
    const uintptr_t addr  = reinterpret_cast<uintptr_t>(dst);
    const size_t    head  = (A - (addr & (A - 1))) & (A - 1);
    const uint8_t*  current_src = src;
 
    // Выравниваем dst на 32 байта
    if (head) [[unlikely]]
    {
        std::memcpy(dst, current_src, head);
        dst         += head;
        current_src += head;
        bytes       -= head;
    }
 
    auto* aligned_dst = std::assume_aligned<32>(dst);
    assert((reinterpret_cast<uintptr_t>(aligned_dst) & 31) == 0);
    size_t qwords     = bytes >> 5;
 
    // 256 байт за итерацию, prefetch на 2 блока вперёд
    while (qwords >= 8) {
        PREFETCH_HINT((current_src + 256), true, PrefetchHint::NTA);
        PREFETCH_HINT((current_src + 320), true, PrefetchHint::NTA);
 
        _mm256_stream_si256(reinterpret_cast<__m256i*>(aligned_dst +   0), _mm256_loadu_si256(reinterpret_cast<const __m256i*>(current_src +   0)));
        _mm256_stream_si256(reinterpret_cast<__m256i*>(aligned_dst +  32), _mm256_loadu_si256(reinterpret_cast<const __m256i*>(current_src +  32)));
        _mm256_stream_si256(reinterpret_cast<__m256i*>(aligned_dst +  64), _mm256_loadu_si256(reinterpret_cast<const __m256i*>(current_src +  64)));
        _mm256_stream_si256(reinterpret_cast<__m256i*>(aligned_dst +  96), _mm256_loadu_si256(reinterpret_cast<const __m256i*>(current_src +  96)));
        _mm256_stream_si256(reinterpret_cast<__m256i*>(aligned_dst + 128), _mm256_loadu_si256(reinterpret_cast<const __m256i*>(current_src + 128)));
        _mm256_stream_si256(reinterpret_cast<__m256i*>(aligned_dst + 160), _mm256_loadu_si256(reinterpret_cast<const __m256i*>(current_src + 160)));
        _mm256_stream_si256(reinterpret_cast<__m256i*>(aligned_dst + 192), _mm256_loadu_si256(reinterpret_cast<const __m256i*>(current_src + 192)));
        _mm256_stream_si256(reinterpret_cast<__m256i*>(aligned_dst + 224), _mm256_loadu_si256(reinterpret_cast<const __m256i*>(current_src + 224)));
 
        aligned_dst += 256;
        current_src += 256;
        qwords      -= 8;
    }
 
    for (; qwords > 0; --qwords)
    {
        _mm256_stream_si256(reinterpret_cast<__m256i*>(aligned_dst), _mm256_loadu_si256(reinterpret_cast<const __m256i*>(current_src)));
        aligned_dst += 32;
        current_src += 32;
    }
 
    const size_t tail = bytes & 31;
    if (tail) [[unlikely]]
    {
        std::memcpy(aligned_dst, current_src, tail);
    }
 
    if constexpr (Fence)
    {
        _mm_sfence();
    }
}
Цитата Сообщение от vlisp Посмотреть сообщение
Это типичное заблуждение дилетанта-велосипедиста.
как вы выразились, пишут оптимизированный вылизанный код! Кармак тоже дилетант-велосипедист. "А все вот это вот я доверяю оптимизации компилятора" мне вас жаль. современные компиляторы хорошо оптимизируют код не спорю (особенно на уровне стандартных библиотек). самый лучший оптимизатор чистый ассемблер если пошло на то! я скажу из личного опыта. я когда-то писал жёсткий реалтайм! оборудование использовалось для исследований в ядерной физике и там критические оптимизации в коде я писал даже не на си, а на голом ассемблере! дёргая за яйца регистры. это оправдано ведь даже порядок команд в ассемблерном коде (инструкций) может вызывать неожиданные результаты. и из-за этого я с коллегой вторым программистом два дня не мог понять почему в плис в старшем байте регистра данных для него (внутри плис) иногда прилетал мусорный старший БИТ с микроконтроллера. и второе из практики код идеально написанный на си с максимальными оптимизациями компилятора проигрывал ассемблеру по скорости и стабильности (очень наглядно я видел на осциллографе программно написанный протокол передачи между устройствами КАМАК где плавали тайминги и заваливались фронты в пакете данных)! и я когда пришёл из "железного" программирования в мир "больших" компьютер просто ужаснулся на сколько людям плевать на свой код! многие даже не понимают что код на с++ может в некоторых случаях работать даже в 50 раз быстрее. так как я си-шник мне трудно осваивать с++ поэтому когда возникают вопросы спрашиваю на форумах. а любовь к велосипедам у меня ещё с той старой работы с железом. а на скрине то над чем я работаю!
Миниатюры
Быстрая функция копирования блоков памяти memcpy  с использованием AVX инструкций  
0
Заблокирован
23.04.2026, 16:03
Слишком много
Цитата Сообщение от Signum7 Посмотреть сообщение
!
Не нужно так нервничать. Побереги здоровье.

Цитата Сообщение от Signum7 Посмотреть сообщение
мне трудно осваивать с++
В С++ - оптимизация не главное.
И этап оптимизации - один из последних.
А первый(и на протяжении почти всей разработки) это - проектирование.
Можно писать эффективный код на С++.
Но сначала нужно освоить его базовые принципы и не гоняться за байтами и тактами.

Цитата Сообщение от Signum7 Посмотреть сообщение
Но совсем другое дело когда нужно непрерывным потоком гнать данные из ОЗУ через PCI шину в ВОЗУ.
Если заниматься такой ерундистикой, то времени останется как раз для того что бы "гонять данные".
Да и где доказательства, что велосипед быстрее?
И даже, если на доли быстрее, то он проигрывает по множеству других критерий(переносимости, надёжности, поддержке, повторному использовании и так далее).
0
 Аватар для Наталья8
638 / 396 / 68
Регистрация: 09.03.2016
Сообщений: 4,374
23.04.2026, 17:38
Цитата Сообщение от 000d60 Посмотреть сообщение
Но сначала нужно освоить его базовые принципы и не гоняться за байтами и тактами.

Не по теме:

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

0
 Аватар для Наталья8
638 / 396 / 68
Регистрация: 09.03.2016
Сообщений: 4,374
23.04.2026, 17:44

В чём не понимаю, в том не понимаю. Последняя карта крякнула... Зараза.
Мне вообще кажеться - видеокарта это такое развлечение на полгода.
И на помойку.
Всё понимаю, когда чип циклично разогреваеться под восемьдясят,
и остывает до сорока, эта хрень долго не протянет.
0
23.04.2026, 17:51

Не по теме:

Signum7,
Фар край четыре не ты написал?
Хорошая игра.

0
1980 / 836 / 115
Регистрация: 01.10.2012
Сообщений: 5,222
Записей в блоге: 2
23.04.2026, 20:00
Цитата Сообщение от Signum7 Посмотреть сообщение
а любовь к велосипедам у меня ещё с той старой работы с железом.
Подумайте о тех кто унаследует/продолжит Ваш код. Что они о Вас скажут, какие теплые слова?
Цитата Сообщение от Signum7 Посмотреть сообщение
а на скрине то над чем я работаю!
Попытка решить игровые проблемы низкоуровневой оптимизацией явно наивна. Да, возможно удастся ускорить некоторые вещи, но их доля будет лишь несколько %, обычно меньше, "зато" оптимизация сожрет массу сил и времени. Производительность определяется тем насколько удачны/вписываются используемые техники. Напр на Вашем скриншоте не видно бампа, вот им и надо заниматься. Освещенность слабовата и.т.п. Нечего сачковать с AVX
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
23.04.2026, 20:00

Нужна функция быстрого копирования блока памяти
Что то типа такого: void Mcopy(POINTER adr1,POINTER adr2,INT_L len){ asm{ mov esi, adr1;// -откуда mov edi, adr2;// -куда ...

Сравнение SSE и AVX. Почему AVX медленнее?
Написал такой код для теста: void SSETest(float* v1, float* v2, float* r) { for (int i = 0; i &lt; 2147483647; i++) { ...

Проверка числа на простоту с использованием AVX C++
Добрый вечер. Я новичок в программировании, поэтому обыденные для программиста вещи даются мне с большим трудом. Несколько дней не...

AVX инструкции с использованием хидера imminitrin.h
Добрый день! Есть простенький код, который проверят 28 бит регистра RCX (ECX не использую, т.к. сборка 64 битная, а в 32 битной можно не...

Многократное использование блоков одних и тех же инструкций в одном обработчике
Как правильнее организовать выполнение периодически повторяющихся инструкций в коде? Функции? Правильно ли это? Если речь о всего...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
32
Ответ Создать тему
Новые блоги и статьи
Запустил конкурс "тем и промптов для текстовых квестов созданных почти чисто ИИ"
Adler 06.10.2026
Всем привет! За последние три-четыре дня я создал более 16 текстовых квестовых игр используя преимущественно по одному запросу к ИИ на игру. Мне так понравилось смотреть все ветки/ сцены во всех. . .
ИИ не может найти нужный язык в списке
Supersumestria 05.10.2026
Я ему даю вот такое изображение и прошу найти и подчеркнуть немецкий язык. Возвращает он вот это: https:/ / i. **********/ vqBWLe2. png Нужную строчку в 3й колонке просто выдумал. . Это. . .
Новая последняя моя музыка в SUNO
zorxor 05.10.2026
Здравствуйте, дорогие мои друзья! С большой радостью я хотел бы представить вам свою новую последнею музыку, которую сгенерировала мне по моей просьбе нейросеть SUNO. С уважением, zorxor. Это. . .
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js. В помощники взял Яндекс-Алису. Было создано три зала на разные интересы. исторические и ретро сериал Хичкок. . .
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru