Объединение двух буферов памяти без memcpy - PullRequest
2 голосов
/ 12 февраля 2009

В C у меня есть функция foo (char *), которая принимает указатель памяти. в звонилке у меня есть два разных буфера памяти, который мне нужно объединить, чтобы я мог передать один указатель foo (). Есть ли способ для меня сделать это без фактического копирования одного буфера до конца другого буфера и без изменения самого foo ()? Т.е. сделать так, чтобы два буфера выглядели как один виртуальный непрерывный буфер для foo ()

Мне это нужно по соображениям производительности. решение O (n) (где n - длина буфера) не приемлемо для моего случая. Кроме того, решение для Linux подходит, если оно помогает.

Спасибо. Nir

Ответы [ 9 ]

5 голосов
/ 13 февраля 2009

Этот вопрос, кажется, задает вопрос, возможно ли объединить содержимое двух буферов (A и B) со следующими ограничениями:

  • Вы не можете скопировать содержимое A или B.
  • Вы не можете изменить адрес A.
  • Операция должна иметь сложность в худшем случае
  • Предположительно, адрес B таков, что A и B еще не соединены. (Как отметил в своем ответе Дж.Ф. Себастьян, если вы, во-первых, можете выделить как А, так и В в первую очередь, то все готово. Но это похоже на вырожденный случай.)
  • Вы должны быть в состоянии сделать это из драйвера ядра Linux (см. Комментарий под ответом Джерома).
  • Ни A, ни B не выровнены по страницам (см. Комментарий под оригинальным вопросом).
  • Ни A, ни B не кратны размеру страницы (см. Комментарий под оригинальным вопросом).

Учитывая все это, мой ответ - нет: это невозможно.

Да, ядро ​​ОС может использовать MMU ЦП (модуль управления памятью на архитектурах, которые его имеют) для переназначения памяти либо в виртуальном адресном пространстве ядра, либо в виртуальном адресном пространстве пользователя. Выделите непрерывный фрагмент виртуального адресного пространства, затем переназначьте A и B в этот буфер, изменив записи таблицы страниц для фрагмента виртуального адресного пространства, указав на физические адреса A и B.

Это не меняет виртуальный адрес A как таковой (поскольку старый виртуальный адрес все еще действителен), но он требует доступа к нему через другой виртуальный адрес. Это может быть проблемой.

Степень детализации этого перераспределения в современных типичных архитектурах ЦП основана на размерах страниц, и, поскольку А и В не выровнены и не кратны размеру страницы, вы не сможете их сделать. полностью выстроиться Это определенно проблема.

Для преобразования N байтов требуется изменить хотя бы одну запись таблицы страниц на каждые M байтов, где M - размер страницы. Это означает, что операция переотображения в любом случае имеет вычислительную сложность O (n). Другие операции, такие как выделение большего количества физических страниц для таблиц страниц, очистка кэшей и TLB и т. Д., Могут иметь дополнительные последствия для производительности.

Кроме того, мне интересно, если цель этого вопроса каким-то образом связана с DMA (прямой доступ к памяти). При выполнении DMA с архаичным устройством, которое требует непрерывной памяти, никакое количество переназначения не поможет, если у вас нет IOMMU в вашем распоряжении. И современному устройству, которое может выполнять DMA-сборку с разбросом, в первую очередь не потребуются непрерывные буферы.

5 голосов
/ 12 февраля 2009

Да, есть способ.

Выделите память для буферов таким образом, чтобы они были смежными в памяти.

Пример:

char* a = malloc(a_size + b_size);
char* b = a + a_size;
3 голосов
/ 12 февраля 2009

Извиняюсь за краткость ответа, но нет, вы не можете.

Как ты сам сказал, тебе либо нужно

  • Выделите один большой буфер и скопируйте отдельные буферы для этого или
  • Измените foo, чтобы использовать несколько указателей.
1 голос
/ 12 февраля 2009

Вы можете попробовать добавить еще один слой косвенности. Вам потребуется переписать foo, чтобы получить массив char * s, и иметь возможность обрабатывать граничное условие между строками.

void foo(char **, int nstrings)
{ 
}

Тогда объединение строк - это всего лишь вопрос создания массива указателей:

char *strings[2] = { string1, string2 };
foo (strings, 2);
1 голос
/ 12 февраля 2009

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

Почему вы выделяете два буфера, когда знаете, что вам понадобится один после этого? А насколько большие буферы? Почему вы избегаете копирования? Вы измерили, что это будет узким местом?

1 голос
/ 12 февраля 2009

Нет, для этого нет общего решения.

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

0 голосов
/ 18 декабря 2014

посмотрите на использование realloc. Как только вы получите второй буфер, вы можете вызвать realloc, чтобы увеличить размер первого буфера. Мой опыт работы с Mac OS заключается в том, что это сильно оптимизировано.

0 голосов
/ 12 февраля 2009

Ниже приведено довольно грязное решение, но, возможно, единственное в вашем случае. И это не будет работать во всех случаях (более того, это не предсказуемо).

Вы можете попробовать использовать mmap. При вызове mmap вы даете ему адрес. mmap попытается выделить память по указанному вами адресу.

Возможно, это лучшее решение, которое вы можете иметь. Вам нужно будет скопировать только один char[], и ни того, ни другого.

Возможно, вам придется стереть символ \0 в конце первого.

И вы можете использовать флаг MAP_FIXED: если mmap не может использовать адрес, он не выделит место в памяти и не выдаст ошибку.

например.

char a[20];
char b[20];

mmap(a + 20, 20, PROT_WRITE, MAP_FIXED, 0, 0);
0 голосов
/ 12 февраля 2009

Можно ли изменить foo(), чтобы он использовал некоторый дескриптор, который описывает список областей памяти, на которые нужно воздействовать (например, указатель на массив структур с парами указатель / длина)?

Таким образом, нет необходимости в операции копирования O (N).

Если это возможно, кажется, что это единственное разумное решение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...