Избегание memcpy в буферизованном выводе C

#c #linux #fstream #buffered

#c #linux #fstream #буферизованный

Вопрос:

Я пытаюсь записать непосредственно в выходной буфер fstream, чтобы избежать memcpy .

Почему следующий код не работает?

Он компилирует, запускает и выдает выходной файл нужной длины в Linux. Но выходной файл не содержит правильного текста. Также обратите внимание, что по какой-то причине, когда я комментирую две строки, включающие str2 , создается выходной файл нулевой длины.

Примечание: В этом примере не удается избежать memcpy , но если это сработает, это поможет мне избежать memcpy в моем приложении.

 #include <fstream>

int main(int argc, char *argv[]) {
  std::fstream out;
  char buffer[512];
  out.rdbuf()->pubsetbuf(buffer, 512);
  out.open("file.txt", std::fstream::out);
  char *str1 = "test text.";
  strcpy(buffer, str1);
  out.rdbuf()->pubseekpos(strlen(str1), std::ios_base::out);
  char *str2 = "why?";
  out << str2;
  out.flush();
  out.close();
}
  

Комментарии:

1. to avoid a memcpy — извините, но это просто глупо. memcpy это одна из самых быстрых операций.

2. Вы действительно уверены, что стоимость memcpy действительно имеет значение по сравнению со стоимостью фактического выполнения ввода-вывода? Вы профилировали его? Это может быть просто преждевременной оптимизацией, и вам следует сосредоточить свои усилия в другом месте.

3. Есть несколько комментариев относительно того, «почему» я это делаю. Давайте пока оставим это в стороне. Суть в том, что код не создает выходной файл с текстом: «тестовый текст. почему?» Кто-нибудь может сказать мне, почему?

4. @Xeo: Вы не только раздражаете, отвечая на вопрос, который не был задан, вы также ошибаетесь. Несмотря на то, что memcpy () быстр, его стоимость измерима, и его устранение может дать заметный прирост производительности, например, 10%, который я измерил в написанном мной анализаторе: article.gmane.org/gmane.comp.lang.lua.general/77955

5. @Josh: «отвечая» — я поместил это как комментарий именно потому, что это не ответ.

Ответ №1:

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

Тот факт, что вы копируете что-либо в буфер, не сообщая потоку, ничего не дает вам в файле.

Как вы могли заметить, seekpos предназначен для позиционирования в файле, а не для перемещения в буфере. Буфер предназначен только для внутреннего использования потока!

Ответ №2:

При out.rdbuf()->pubseekpos(strlen(str1), std::ios_base::out); этом потоку предлагается пропустить пересылку через вновь созданный файл. Можете ли вы ожидать, что он будет извлекать данные из указанного вами буфера? Подсказка: видели ли вы какие-либо примеры, включающие указание буфера, в котором говорилось, что вам нужно каким-либо образом инициализировать его или указать начальное количество содержащихся в нем символов, не содержащих мусора? Нет … потому что сам поток отслеживает, какие части буфера используются. Следовательно, когда вы берете предполагаемый пустой буфер и пропускаете вперед, он генерирует промежуточные значения NUL. Для них не имело бы смысла устанавливать их в буфере (поэтому простое выполнение a memcpy после pubseekpos также не сработает) — что, если вы перескочили больше, чем размер буфера вперед?

Надеюсь, это послужит хотя бы иллюстрацией проблемы, хотя на данном этапе я не задумывался о том, как вы могли бы заставить поток изменить его «отслеживание» значимого содержимого буфера….

Ответ №3:

Пока еще рано, но разве вы в основном не делаете

 #include <fstream>

int main(int argc, char *argv[]) 
{

  std::fstream out;
  out.open("file.txt", std::fstream::out);

  out << "test text";
  out << "why?";

  out.flush();
  out.close();
}
  

?

Комментарии:

1. В этом примере, да! Но могу ли я получить прямой доступ (указатель на чтение) к выходному буферу?

2. чего вы хотите этим добиться? поскольку вы не вызываете никакой операции записи, я почти уверен, что некоторая реализация ничего не запишет. вы манипулируете памятью, но не записываете в файл. Что вы на самом деле хотите сделать?

3. @user Нет, но я верю, что вы можете скопировать его в кучу.

Ответ №4:

Я недостаточно знаком с библиотекой C iostream, чтобы сказать, что не так с вашей программой, я бы только упомянул, что если вы хотите выполнить свою собственную буферизацию, вероятно, было бы проще использовать интерфейсы read () и write () напрямую (man 2 write). Если вы выполняете свою собственную буферизацию, библиотеки iostream, вероятно, не принесут вам много пользы и только затуманят то, что происходит на самом деле.

Ответ №5:

Я вижу три возможности

  1. Реализуйте свой собственный класс stream-buffer
  2. Используйте собственный файловый API
  3. Отключите буферизацию с помощью pubsetbuf(0, 0)

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