Re: Что отрабоатает быстрее?
От: Аноним  
Дата: 06.05.05 11:18
Оценка:
Здравствуйте, temik, Вы писали:

T> В важном учаске кода надо произвести копирование из одного буфера в другой. что отработает быстрее?

T>memcpy(dest,src,size)
T>или
T>for(int i=o,i<size;i++)
T> dest[i]=src[i];

T>буферы хранят данные типа BYTE. Размер буферов порядка 10-15 мегабайт


если x86 платформа, то лучше всего использовать movntq — поищи топ VuDZ'a — сравнение скорости и пр.
код примерно такой (только prefetch не совсем правильно используется)

// ==================================================================
// 
// FUNCTION :  SSEmemcpy()
// 
// * Description :
//     fast copy with mmx sse - faster than memcpy in 3-4 times
// 
// * Author : [Vadim VuDZ]
// 
// * Returns : [void] - nothing
// 
// * Function parameters : 
// [to]   - pointer to destination
// [from] - pointer to source
// [sz]   - lenght of [from]
// 
// ==================================================================

void SSEmemcpy(void * to, const void * from, unsigned int sz){
                // check for correct values
                if (!to || !from || !sz) {
#ifdef  DEBUG
                                OutputDebugString("SSEmemcpy: Incorrect parameters!\n");
#endif
                                return;
                }

                // Check for SSE
                if (!_IsSSE) {
                                MMXmemcpy(to, from, sz);
                                return;
                }

                int ne32 = sz % 32;
                int szz = sz - ne32;

                if (szz != 0){
                                __asm
                                {
                                               pusha;
                                               mov esi, from;
                                               mov ecx, szz;
                                               prefetcht0 [esi + ecx - 32];                // preload first 32 bytes
                                               mov edi, to;
                                               shr ecx, 3;            // devide on 8
_l_p3:                     movq mm0, [esi + ecx * 8 - 8];      
                                               movq mm1, [esi + ecx * 8 - 16];
                                               movq mm2, [esi + ecx * 8 - 24];
                                               movq mm3, [esi + ecx * 8 - 32];
                                               prefetcht0 [esi + ecx * 8 - 64];// preload next 32 bytes
                                               movntq [edi + ecx * 8 - 8], mm0;
                                               movntq [edi + ecx * 8 - 16], mm1;
                                               movntq [edi + ecx * 8 - 24], mm2;
                                               movntq [edi + ecx * 8 - 32], mm3;
                                               sub ecx, 4;
                                               jnz _l_p3;
                                               popa;
                                }
                                __asm emms; // set fpu registers free
                }

                // check for full copy
                memcpy((char*)to + sz - (ne32), (char*)from + sz - (ne32), ne32);
}
Re[4]: Что отрабоатает быстрее?
От: Шахтер Интернет  
Дата: 07.05.05 03:18
Оценка:
Здравствуйте, alnsn, Вы писали:

A>Здравствуйте, Шахтер, Вы писали:


Ш>>Здравствуйте, ArtDenis, Вы писали:


t>>>> for(int i=o,i<size;i++)

t>>>> dest[i]=src[i];

AD>>>Есть вероятность, что оптимизатор для цикла создаст код, аналогичный коду memcpy.


Ш>>Нету вероятности. Дело в том, что компилятор не знает, что буфера не пересекаются, и сказать ему об этом невозможно -- в С++ пока нет необходимых выразительных средств (в С99 есть ключевое слово restrict для этого). А без этого предположения данный цикл семантически не эквивалентен memcpy.


A>В данном случае это неприменимо, но оптимизация, похожая на restrict возможна: http://www.ddj.com/documents/s=880/ddj0010d/


Я извиняюсь, но не надо, пожалуйста, приводить ссылки, которые нельзя прочитать без регистрации и.т.п. фигни. Я их не читаю и подобные сайты сразу заношу в черный список.
... << RSDN@Home 1.1.3 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[4]: Что отрабоатает быстрее?
От: Шахтер Интернет  
Дата: 07.05.05 03:18
Оценка:
Здравствуйте, ArtDenis, Вы писали:

AD>Здравствуйте, Шахтер, Вы писали:


Ш>>Нету вероятности. Дело в том, что компилятор не знает, что буфера не пересекаются, и сказать ему об этом невозможно -- в С++ пока нет необходимых выразительных средств...


AD>Не надо путуть memmove и memcpy. memcpy по поведению аналогичен вышеприведённому циклу.


Действительно, не надо. А ещё неплохо бы подучить матчасть и узнать, что memcpy не работает для пересекающихся блоков данных -- его поведение в этом случае неопределено, поэтому он НЕ эквивалентен циклу.

7.21.2.1 The memcpy function

Synopsis

#include <string.h>

void *memcpy(void * restrict s1,
const void * restrict s2,
size_t n);

Description

The memcpy function copies n characters from the object pointed to by s2 into the
object pointed to by s1. If copying takes place between objects that overlap, the behavior
is undefined.
... << RSDN@Home 1.1.3 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[5]: Что отрабоатает быстрее?
От: ArtDenis Россия  
Дата: 07.05.05 05:41
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Действительно, не надо. А ещё неплохо бы подучить матчасть и узнать, что memcpy не работает для пересекающихся блоков данных -- его поведение в этом случае неопределено, поэтому он НЕ эквивалентен циклу.


Подойду с другой стороны. А где в этом коде

  for(int i=0,i<size;i++) dest[i]=src[i];


гарантия того, что если dest и src пересекаются, то перенос данных пройдёт корректно?

Ш>The memcpy function copies n characters from the object pointed to by s2 into the

Ш>object pointed to by s1. If copying takes place between objects that overlap, the behavior
Ш>is undefined.
И я о том же
... << Rsdn@Home 1.1.4 beta 1 >>
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[6]: Что отрабоатает быстрее?
От: Шахтер Интернет  
Дата: 07.05.05 21:03
Оценка:
Здравствуйте, ArtDenis, Вы писали:

AD>Здравствуйте, Шахтер, Вы писали:


Ш>>Действительно, не надо. А ещё неплохо бы подучить матчасть и узнать, что memcpy не работает для пересекающихся блоков данных -- его поведение в этом случае неопределено, поэтому он НЕ эквивалентен циклу.


AD>Подойду с другой стороны. А где в этом коде


AD>
AD>  for(int i=0,i<size;i++) dest[i]=src[i];
AD>


AD>гарантия того, что если dest и src пересекаются, то перенос данных пройдёт корректно?


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

Ш>>The memcpy function copies n characters from the object pointed to by s2 into the

Ш>>object pointed to by s1. If copying takes place between objects that overlap, the behavior
Ш>>is undefined.
AD>И я о том же
... << RSDN@Home 1.1.3 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[7]: Что отрабоатает быстрее?
От: ArtDenis Россия  
Дата: 08.05.05 06:30
Оценка:
Здравствуйте, Шахтер, Вы писали:

AD>>Подойду с другой стороны. А где в этом коде

AD>>
AD>>  for(int i=0,i<size;i++) dest[i]=src[i];
AD>>

AD>>гарантия того, что если dest и src пересекаются, то перенос данных пройдёт корректно?

Ш>Здесь не будет переноса данных, вообще говоря.

Извиняюсь за ошибку. Конечно же я имел ввиду копировние

Ш>Тем не менее, это полностью легальный код, имеющий свою, вполне определённую семантику. Ты не можешь при оптимизации заменить его на что-то, что делает неизвестно что. Компилятор не вправе строить догадки, что имел ввиду программист при написании кода.

Не вижу ни одного доказательства, чем memcpy по поведению отличается от цикла. Для меня из поведение абсолютно идентично, т.к.
1. Побайтно копирует данные из источника в приёмник
2. Не гарантирует, что данные будут корректно скопированы, если источник и приёмник пересекаются
3. Нет предположений, что данные приёмника должны синхронизироваться для доступа из разных потоков во время копирования данных

Я не вижу припятствий, почему оптимизатор не может преобразовать код цикла в код, аналогичный memcpy. По крайней мере этот пример подтвержадет мои слова (vc toolkit 2003).

Исходный код:
#include <memory.h>
#include <iostream>

const int size = 256;

// Ф-я используется для подавления оптимизации
// Без неё из кода будет вырезано всё, что не связано с volatile
char calc(const char * data)
{
  char result = 0;
  for (int i = 0; i < size; i++) result += data[i];
  return result;
}


int main(int argc, char* argv[])
{
  const char src[size] = {0};
  char dst1[size], dst2[size];
  volatile char dstv[size];
  
  for (int i = 0; i < size; i++) dst1[i] = src[i];
  
  memcpy(dst2, src, size);
  
  for (int i = 0; i < size; i++) dstv[i] = src[i];
  
  std::cout << calc(dst1)+calc(dst2) << "\n";
}


Третий случай — с volatile я привёл, чтобы показать, как оптимизатор ведет себя, когда программист конкретно указывает, что к dstv во время копировния может получить дотуп другой процесс или поток.

А вот что получается при оптимизации с ключём O2:
...
; for (int i = 0; i < size; i++) dst1[i] = src[i];
    mov    ecx, 64                    ; 00000040H
    lea    esi, DWORD PTR _src$[esp+1040]
    lea    edi, DWORD PTR _dst1$[esp+1040]
    rep movsd
; memcpy(dst2, src, size);
    mov    ecx, 64                    ; 00000040H
    lea    esi, DWORD PTR _src$[esp+1040]
    lea    edi, DWORD PTR _dst2$[esp+1040]
    rep movsd
; for (int i = 0; i < size; i++) dstv[i] = src[i];
    xor    eax, eax
    npad    13
$L8472:
    mov    cl, BYTE PTR _src$[esp+eax+1040]
    mov    BYTE PTR _dstv$[esp+eax+1040], cl
    inc    eax
    cmp    eax, 256                ; 00000100H
    jl    SHORT $L8472
...


строки
for (int i = 0; i < size; i++) dst1[i] = src[i];

и
memcpy(dst2, src, size);

обрабатываются аналогично.

А строка
for (int i = 0; i < size; i++) dstv[i] = src[i];

не оптимизируется потому, что компилятору явно указали, что к dstv во время копирования может получить доступ ещё один поток или процесс. Если бы компилятор оптимизировал эту строчку аналогично предыдущим, то это бы не давало возможности программисту вставить код для синхронизации содержимого dstv из разных потоков.
... << Rsdn@Home 1.1.4 beta 1 >>
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[5]: Что отрабоатает быстрее?
От: alnsn Великобритания http://nasonov.blogspot.com
Дата: 08.05.05 10:15
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Я извиняюсь, но не надо, пожалуйста, приводить ссылки, которые нельзя прочитать без регистрации и.т.п. фигни. Я их не читаю и подобные сайты сразу заношу в черный список.


Когда я ее читал, она была бесплатная и без регистрации. Сейчас поискал, нету такой ссылки. Но мне показалось, что можно бесплатно зарегистрироваться на 6 месяцев и почитать.
Re[8]: Что отрабоатает быстрее?
От: Шахтер Интернет  
Дата: 08.05.05 22:23
Оценка:
Здравствуйте, ArtDenis, Вы писали:

AD>Здравствуйте, Шахтер, Вы писали:


AD>>>Подойду с другой стороны. А где в этом коде

AD>>>
AD>>>  for(int i=0,i<size;i++) dest[i]=src[i];
AD>>>

AD>>>гарантия того, что если dest и src пересекаются, то перенос данных пройдёт корректно?

Ш>>Здесь не будет переноса данных, вообще говоря.

AD>Извиняюсь за ошибку. Конечно же я имел ввиду копировние

Ш>>Тем не менее, это полностью легальный код, имеющий свою, вполне определённую семантику. Ты не можешь при оптимизации заменить его на что-то, что делает неизвестно что. Компилятор не вправе строить догадки, что имел ввиду программист при написании кода.

AD>Не вижу ни одного доказательства, чем memcpy по поведению отличается от цикла.

Отличается тем, что его (memcpy) поведение не определено. В отличие от цикла.

AD>Для меня из поведение абсолютно идентично, т.к.

AD>1. Побайтно копирует данные из источника в приёмник

Нет. Цикл выполнит последовательность присваиваний

dst[0] = src[0] ;
dst[1] = src[1] ;
dst[2] = src[2] ;
dst[3] = src[3] ;
...
dst[size-1] = src[size-1] ;


Что сделает memcpy -- всё что угодно, вплоть до форматирования винчестера.

Суть дела тут в том, что если источник и приёмник не пересекаются, то последовательность присвоений, указаных выше, является последовательностью независимых действий, т.е. эти операции можно произвольным образом менять местями, группировать и даже выполнять параллельно (если процессор обладает соотвествующей способностью). Т.е. открываются возможности для оптимизации, именно эти возможности реализуются в memcpy с учётом возможностей процессора. Именно поэтому, результат выполнения memcpy над пересекающимися данными непредсказуем.

AD>2. Не гарантирует, что данные будут корректно скопированы, если источник и приёмник пересекаются


Это утверждение нерелевантно. А кто сказал, что цикл должен копировать данные? Цикл делает ровно то, что он делает (см. выше). Совпадает это с ожиданиями программиста или нет -- это вопрос к программисту. Компилятор не обладает телепатическими способностями.

AD>Я не вижу припятствий, почему оптимизатор не может преобразовать код цикла в код, аналогичный memcpy. По крайней мере этот пример подтвержадет мои слова (vc toolkit 2003).


Нет. Этот пример подотверждает только то, что в VC хороший оптимизатор. Он смог прочухать в данном случае, что источник и приёмник данных не пересекаются (поскольку они известны в компил-тайм) и сделал соответствующую оптимизацию.

Вот адекватный пример, демонстрирующий разницу.

/* main.cpp */ 

#include <iostream>

using namespace std;

/* main() */ 

int main()
 {
  char mem[10]={'a','b'};
  
  char *dst=mem+2;
  const char *src=mem;
  size_t size=8;

#if 1
  
  for(size_t i=0; i<size ;i++) dst[i]=src[i]; // печатает ababababab
  
#else

  memcpy(dst,src,size); //  у меня печатает abab, у тебя может форматнуть винчестер

#endif  
  
  cout.write(mem,10) << endl ;
  
  return 0;
 }
... << RSDN@Home 1.1.3 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[9]: Что отрабоатает быстрее?
От: ArtDenis Россия  
Дата: 09.05.05 09:41
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>...Нет. Цикл выполнит последовательность присваиваний

Ш>
Ш>dst[0] = src[0] ;
Ш>dst[1] = src[1] ;
Ш>dst[2] = src[2] ;
Ш>dst[3] = src[3] ;
Ш>...
Ш>dst[size-1] = src[size-1] ;
Ш>

Ш>Что сделает memcpy -- всё что угодно, вплоть до форматирования винчестера. ...

С этим я, пожалуй, соглашусь
Но с другой стороны: vc оптимизирует цикл и memcpy в одинаковый код...
... << Rsdn@Home 1.1.4 beta 1 >>
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[10]: Что отрабоатает быстрее?
От: Шахтер Интернет  
Дата: 11.05.05 00:36
Оценка:
Здравствуйте, ArtDenis, Вы писали:

AD>Здравствуйте, Шахтер, Вы писали:


Ш>>...Нет. Цикл выполнит последовательность присваиваний

Ш>>
Ш>>dst[0] = src[0] ;
Ш>>dst[1] = src[1] ;
Ш>>dst[2] = src[2] ;
Ш>>dst[3] = src[3] ;
Ш>>...
Ш>>dst[size-1] = src[size-1] ;
Ш>>

Ш>>Что сделает memcpy -- всё что угодно, вплоть до форматирования винчестера. ...

AD>С этим я, пожалуй, соглашусь

AD>Но с другой стороны: vc оптимизирует цикл и memcpy в одинаковый код...

Разве?

; 11   :   char mem[10]={'a','b'};

    mov    al, 97                    ; 00000061H
    push    esi
    xor    edx, edx
    mov    DWORD PTR _mem$[esp+18], edx
    mov    cl, 98                    ; 00000062H
    mov    DWORD PTR _mem$[esp+22], edx
    push    edi
    mov    BYTE PTR _mem$[esp+20], al

; 12   :   
; 13   :   char *dst=mem+2;
; 14   :   const char *src=mem;
; 15   :   size_t size=8;
; 16   : 
; 17   : #if 1
; 18   :   
; 19   :   for(size_t i=0; i<size ;i++) dst[i]=src[i];

    mov    BYTE PTR _mem$[esp+22], al
    mov    BYTE PTR _mem$[esp+24], al
    mov    BYTE PTR _mem$[esp+26], al
    mov    BYTE PTR _mem$[esp+28], al

; 20   :   
; 21   : #else
; 22   : 
; 23   :   memcpy(dst,src,size);
; 24   : 
; 25   : #endif  
; 26   :   
; 27   :   cout.write(mem,10) << endl ;

    push    10                    ; 0000000aH
    lea    eax, DWORD PTR _mem$[esp+24]
    mov    BYTE PTR _mem$[esp+25], cl
    mov    BYTE PTR _mem$[esp+27], cl
    mov    BYTE PTR _mem$[esp+29], cl
    mov    BYTE PTR _mem$[esp+31], cl
    mov    BYTE PTR _mem$[esp+33], cl


; 11   :   char mem[10]={'a','b'};

    xor    eax, eax
    mov    DWORD PTR _mem$[esp+14], eax
    push    esi
    mov    BYTE PTR _mem$[esp+16], 97        ; 00000061H
    mov    BYTE PTR _mem$[esp+17], 98        ; 00000062H

; 12   :   
; 13   :   char *dst=mem+2;
; 14   :   const char *src=mem;
; 15   :   size_t size=8;
; 16   : 
; 17   : #if 0
; 18   :   
; 19   :   for(size_t i=0; i<size ;i++) dst[i]=src[i];
; 20   :   
; 21   : #else
; 22   : 
; 23   :   memcpy(dst,src,size);

    mov    ecx, DWORD PTR _mem$[esp+16]
    mov    DWORD PTR _mem$[esp+22], eax
    mov    DWORD PTR _mem$[esp+18], ecx
    mov    edx, DWORD PTR _mem$[esp+20]

; 24   : 
; 25   : #endif  
; 26   :   
; 27   :   cout.write(mem,10) << endl ;

    push    10                    ; 0000000aH
    lea    eax, DWORD PTR _mem$[esp+20]
    push    eax
    mov    ecx, OFFSET FLAT:?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
    mov    DWORD PTR _mem$[esp+30], edx
... << RSDN@Home 1.1.3 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[11]: Что отрабоатает быстрее?
От: _Семен Россия www.gamedeff.com
Дата: 11.05.05 07:12
Оценка:
afair, IntelC в каждом случае вставляет практически одинаковый код на SSE с non-temporary write для достаточно больших размеров массива, который выигрывает у "классического" memcpy раза в два.
Re[11]: Что отрабоатает быстрее?
От: ArtDenis Россия  
Дата: 12.05.05 02:40
Оценка:
Здравствуйте, Шахтер, Вы писали:

AD>>Но с другой стороны: vc оптимизирует цикл и memcpy в одинаковый код...

Ш>Разве?

Имелись ввиду большие объёмы копируемых данных
... << Rsdn@Home 1.1.4 beta 1 >>
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[12]: Что отрабоатает быстрее?
От: Шахтер Интернет  
Дата: 12.05.05 02:51
Оценка:
Здравствуйте, _Семен, Вы писали:

_С>afair, IntelC в каждом случае вставляет практически одинаковый код на SSE с non-temporary write для достаточно больших размеров массива, который выигрывает у "классического" memcpy раза в два.


Что значит, в каждом случае?
... << RSDN@Home 1.1.3 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[12]: Что отрабоатает быстрее?
От: Шахтер Интернет  
Дата: 12.05.05 02:59
Оценка:
Здравствуйте, ArtDenis, Вы писали:

AD>Здравствуйте, Шахтер, Вы писали:


AD>>>Но с другой стороны: vc оптимизирует цикл и memcpy в одинаковый код...

Ш>>Разве?

AD>Имелись ввиду большие объёмы копируемых данных


Ты знаешь, у меня такое ощущения, что то ли ты меня не понимаешь, то ли я тебя.
Ладно, информацию для размышления я выдал, остальное от меня не зависит.
... << RSDN@Home 1.1.3 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[13]: Что отрабоатает быстрее?
От: Аноним  
Дата: 12.05.05 03:02
Оценка:
Здравствуйте, Шахтер, Вы писали:

_С>>afair, IntelC в каждом случае вставляет практически одинаковый код на SSE с non-temporary write для достаточно больших размеров массива, который выигрывает у "классического" memcpy раза в два.


Ш>Что значит, в каждом случае?


Значит, и в случае использования memcpy, и если написать цикл руками.
Re[14]: Что отрабоатает быстрее?
От: Шахтер Интернет  
Дата: 12.05.05 03:28
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Здравствуйте, Шахтер, Вы писали:


_С>>>afair, IntelC в каждом случае вставляет практически одинаковый код на SSE с non-temporary write для достаточно больших размеров массива, который выигрывает у "классического" memcpy раза в два.


Ш>>Что значит, в каждом случае?


А>Значит, и в случае использования memcpy, и если написать цикл руками.


Прочитай всю ветку с начала.
... << RSDN@Home 1.1.3 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re: Что отрабоатает быстрее?
От: arcman Россия  
Дата: 19.08.05 09:41
Оценка:
Здравствуйте, temik, Вы писали:

T> В важном учаске кода надо произвести копирование из одного буфера в другой. что отработает быстрее?

T>memcpy(dest,src,size)
T>или
T>for(int i=o,i<size;i++)
T> dest[i]=src[i];

T>буферы хранят данные типа BYTE. Размер буферов порядка 10-15 мегабайт


Быстрее будет копирование в цикле по 4ре байта за раз (двойными словами)
Остальные грабли разобраны в книге:
"Крис Касперски — Техника оптимизации программ. Эффективное использование памяти" http://findbook.ru/search/d0?s=1&amp;pvalue=%D2%E5%F5%ED%E8%EA%E0+%EE%EF%F2%E8%EC%E8%E7%E0%F6%E8%E8+%EF%F0%EE%E3%F0%E0%EC%EC&amp;r=0&amp;ptype=1)
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.