В важном учаске кода надо произвести копирование из одного буфера в другой. что отработает быстрее?
memcpy(dest,src,size)
или
for(int i=o,i<size;i++)
dest[i]=src[i];
буферы хранят данные типа BYTE. Размер буферов порядка 10-15 мегабайт
Здравствуйте, 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 мегабайт
Здравствуйте, 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 мегабайт
вариант с memcpy отработает намного быстрее ИМХО, ну уж даже если компилятор каким то образом соптимизирует цикл, то всё равно вариант с memcpy будет не медленнее.
Hello, 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 мегабайт
Я когда то сравнивал memcpy, duff device и цикл. Первые два примерно одинаково, цикл проигрывал существенно.
Здравствуйте, ArtDenis, Вы писали:
AD>А без оптимизации — всё зависит от платформы Под intel — однозначно memcpy будет быстрее.
Под intel — все очень просто: там оптимизированный вариант memcpy (вопрос только, будет ли оптимизированный вариант memcpy использоваться при выключенной оптимизации) использует movnt* команды — как раз чтобы избавиться от проблем с кешем, про которые упомянул MaximE. Так что да, в этом случае memcpy однозначно лучше.
Здравствуйте, temik, Вы писали:
T> В важном учаске кода надо произвести копирование из одного буфера в другой. что отработает быстрее? T>memcpy(dest,src,size) T>или T>for(int i=o,i<size;i++) T> dest[i]=src[i];
Посмотрите, пожалуйста, во что развернётся макрос memcpy и вопрос будет снят.
Если лень, то макрос разворачивается в хитрый асм, быстрее которого придумать ничего не удастся.
Здравствуйте, ArtDenis, Вы писали:
t>> for(int i=o,i<size;i++) t>> dest[i]=src[i];
AD>Есть вероятность, что оптимизатор для цикла создаст код, аналогичный коду memcpy.
Нету вероятности. Дело в том, что компилятор не знает, что буфера не пересекаются, и сказать ему об этом невозможно -- в С++ пока нет необходимых выразительных средств (в С99 есть ключевое слово restrict для этого). А без этого предположения данный цикл семантически не эквивалентен memcpy.
AD>А без оптимизации — всё зависит от платформы Под intel — однозначно memcpy будет быстрее.
Здравствуйте, Antikrot, Вы писали:
A>Здравствуйте, ArtDenis, Вы писали:
AD>>А без оптимизации — всё зависит от платформы Под intel — однозначно memcpy будет быстрее. A>Под intel — все очень просто: там оптимизированный вариант memcpy (вопрос только, будет ли оптимизированный вариант memcpy использоваться при выключенной оптимизации) использует movnt* команды — как раз чтобы избавиться от проблем с кешем, про которые упомянул MaximE. Так что да, в этом случае memcpy однозначно лучше.
Не совсем, правда, коллега. Не так давно занимался оптимизацией копирования памяти для p4-платформ.Выяснилась интересная вещь — оптимизированный memcpy (prefetch, 4xmovntq для протаскивания 32-х байт) дает выигрыш (линейно растуший до 3-х раз по скорости) лишь начиная с объема копирования 50-55 страниц. До этого рулит memcpy. Объяснить эффект довольно просто. Делаем выводы
Здравствуйте, Шахтер, Вы писали:
Ш>Здравствуйте, ArtDenis, Вы писали:
t>>> for(int i=o,i<size;i++) t>>> dest[i]=src[i];
AD>>Есть вероятность, что оптимизатор для цикла создаст код, аналогичный коду memcpy.
Ш>Нету вероятности. Дело в том, что компилятор не знает, что буфера не пересекаются, и сказать ему об этом невозможно -- в С++ пока нет необходимых выразительных средств (в С99 есть ключевое слово restrict для этого). А без этого предположения данный цикл семантически не эквивалентен memcpy.
Здравствуйте, Шахтер, Вы писали:
Ш>Нету вероятности. Дело в том, что компилятор не знает, что буфера не пересекаются, и сказать ему об этом невозможно -- в С++ пока нет необходимых выразительных средств...
Не надо путуть memmove и memcpy. memcpy по поведению аналогичен вышеприведённому циклу.
Здравствуйте, 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 не совсем правильно используется)
Здравствуйте, 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/
Я извиняюсь, но не надо, пожалуйста, приводить ссылки, которые нельзя прочитать без регистрации и.т.п. фигни. Я их не читаю и подобные сайты сразу заношу в черный список.
Здравствуйте, ArtDenis, Вы писали:
AD>Здравствуйте, Шахтер, Вы писали:
Ш>>Нету вероятности. Дело в том, что компилятор не знает, что буфера не пересекаются, и сказать ему об этом невозможно -- в С++ пока нет необходимых выразительных средств...
AD>Не надо путуть memmove и memcpy. memcpy по поведению аналогичен вышеприведённому циклу.
Действительно, не надо. А ещё неплохо бы подучить матчасть и узнать, что memcpy не работает для пересекающихся блоков данных -- его поведение в этом случае неопределено, поэтому он НЕ эквивалентен циклу.
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.
Здравствуйте, Шахтер, Вы писали:
Ш>Действительно, не надо. А ещё неплохо бы подучить матчасть и узнать, что 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.
И я о том же
Здравствуйте, 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>И я о том же
Здравствуйте, Шахтер, Вы писали:
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;
// Ф-я используется для подавления оптимизации
// Без неё из кода будет вырезано всё, что не связано с volatilechar 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 во время копировния может получить дотуп другой процесс или поток.
не оптимизируется потому, что компилятору явно указали, что к dstv во время копирования может получить доступ ещё один поток или процесс. Если бы компилятор оптимизировал эту строчку аналогично предыдущим, то это бы не давало возможности программисту вставить код для синхронизации содержимого dstv из разных потоков.
Здравствуйте, Шахтер, Вы писали:
Ш>Я извиняюсь, но не надо, пожалуйста, приводить ссылки, которые нельзя прочитать без регистрации и.т.п. фигни. Я их не читаю и подобные сайты сразу заношу в черный список.
Когда я ее читал, она была бесплатная и без регистрации. Сейчас поискал, нету такой ссылки. Но мне показалось, что можно бесплатно зарегистрироваться на 6 месяцев и почитать.
AD>>>гарантия того, что если dest и src пересекаются, то перенос данных пройдёт корректно?
Ш>>Здесь не будет переноса данных, вообще говоря. AD>Извиняюсь за ошибку. Конечно же я имел ввиду копировние
Ш>>Тем не менее, это полностью легальный код, имеющий свою, вполне определённую семантику. Ты не можешь при оптимизации заменить его на что-то, что делает неизвестно что. Компилятор не вправе строить догадки, что имел ввиду программист при написании кода. AD>Не вижу ни одного доказательства, чем memcpy по поведению отличается от цикла.
Отличается тем, что его (memcpy) поведение не определено. В отличие от цикла.
AD>Для меня из поведение абсолютно идентично, т.к. AD>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;
}
Ш>>Что сделает memcpy -- всё что угодно, вплоть до форматирования винчестера. ...
AD>С этим я, пожалуй, соглашусь AD>Но с другой стороны: vc оптимизирует цикл и memcpy в одинаковый код...
afair, IntelC в каждом случае вставляет практически одинаковый код на SSE с non-temporary write для достаточно больших размеров массива, который выигрывает у "классического" memcpy раза в два.
Здравствуйте, _Семен, Вы писали:
_С>afair, IntelC в каждом случае вставляет практически одинаковый код на SSE с non-temporary write для достаточно больших размеров массива, который выигрывает у "классического" memcpy раза в два.
Здравствуйте, ArtDenis, Вы писали:
AD>Здравствуйте, Шахтер, Вы писали:
AD>>>Но с другой стороны: vc оптимизирует цикл и memcpy в одинаковый код... Ш>>Разве?
AD>Имелись ввиду большие объёмы копируемых данных
Ты знаешь, у меня такое ощущения, что то ли ты меня не понимаешь, то ли я тебя.
Ладно, информацию для размышления я выдал, остальное от меня не зависит.
Здравствуйте, Шахтер, Вы писали:
_С>>afair, IntelC в каждом случае вставляет практически одинаковый код на SSE с non-temporary write для достаточно больших размеров массива, который выигрывает у "классического" memcpy раза в два.
Ш>Что значит, в каждом случае?
Значит, и в случае использования memcpy, и если написать цикл руками.
Здравствуйте, <Аноним>, Вы писали:
А>Здравствуйте, Шахтер, Вы писали:
_С>>>afair, IntelC в каждом случае вставляет практически одинаковый код на SSE с non-temporary write для достаточно больших размеров массива, который выигрывает у "классического" memcpy раза в два.
Ш>>Что значит, в каждом случае?
А>Значит, и в случае использования memcpy, и если написать цикл руками.
Здравствуйте, 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 мегабайт