Стратегия роста буфера

Я не уверен, что это « не делает этого » тоже ...

Благодаря чрезвычайно полезному обсуждению в https: // unix.stackexchange.com/questions/213799/can-bash-write-to-its-own-input-stream/ ...

Утилита tailcd (для " call cd "), который работает как в bash, так и под Midnight Commander, позволяет использовать в сценариях, таких как

/ bin / mkcd:

mkdir "$1" && tailcd "$1"

Реализация сложна и требует xdotool. Команда tailcd должна быть последней командой в скрипте (это типичное требование совместимости для утилит, допускающих несколько реализаций). Он взламывает входной поток bash, а именно, вставляет cd в него. В случае с Midnight Commander он дополнительно вставляет две команды клавиатуры Ctrl + O (панели вкл. / Выкл.) И очень хакерским образом использует сон для синхронизации между процессами (что является позором, но оно работает).

/ bin / tailcd:

#! /bin/bash
escapedname=`sed 's/[^a-zA-Z\d._/-]/\\\\&/g' <<< "$1"`
if [ -z "$MC_TMPDIR" ] ; then
xdotool type " cd $escapedname  "; xdotool key space Return
else
(sleep 0.1; xdotool type " cd $escapedname "; xdotool key space Return Ctrl+o; sleep 0.1; xdotool key Ctrl+o )&
fi

(Пробел до cd препятствует тому, чтобы вставленная команда переходила в историю, пробелы после имени каталога необходимы для ее работы но я не знаю почему.)

Другая реализация tailcd не использует xdotool, но она не работает с Midnight Commander:

#!/bin/bash
escapedname=`sed 's/[^a-zA-Z\d._/-]/\\\\&/g' <<< "$1"`
perl -e 'ioctl(STDIN, 0x5412, $_) for split "", join " ", @ARGV' " cd" "$escapedname" $'\r'

В идеале tailcd будет / должен быть частью bash, использовать обычную межпроцессную связь и т. д.

13
задан Alexander Gladysh 15 February 2010 в 21:29
поделиться

9 ответов

Если у вас нет веской причины поступить иначе, экспоненциальный рост, вероятно, будет лучшим выбором. Использование 1,5 в качестве показателя степени не совсем волшебно, и на самом деле Эндрю Кениг изначально не так сказал. Первоначально он сказал, что коэффициент роста должен быть меньше (1 + sqrt (5)) / 2 (~ 1,6).

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

Ссылки: Я полагаю, что Эндрю первоначально опубликовал это в журнале ( Journal of Object Oriented Programming , IIRC), который не публиковался уже много лет, так что я получил повторное сообщение. распечатать, вероятно, будет довольно сложно.

Сообщение Эндрю Кенига в Usenet и P.J. Сообщение Plauger's Usenet .

13
ответ дан 1 December 2019 в 19:07
поделиться

Я обычно использую комбинацию сложения небольшой фиксированной суммы и умножения на 1,5, потому что это эффективно для реализации и приводит к разумной ширине шага, которая вначале больше, и более разумной памяти при увеличении буфера. В качестве фиксированного смещения я обычно использую начальный размер буфера и начинаю с довольно небольших начальных размеров:

new_size = old_size + ( old_size >> 1 ) + initial_size;

В качестве начального_размера я использую 4 для типов коллекции, 8, 12 или 16 для строковых типов и от 128 до 4096 для буферов ввода / вывода. в зависимости от контекста.

Вот небольшая диаграмма, которая показывает, что это растет намного быстрее (желтый + красный) на ранних этапах по сравнению с умножением только на 1,5 (красный).

growth chart

Итак, если вы начали со 100, вам потребуется, например, 6 увеличений, чтобы вместить 3000 элементов, а для умножения только на 1,5 потребуется 9.

При больших размерах влияние сложения становится незначительным, что делает оба подхода одинаково масштабируемыми. ну тогда в 1,5 раза. Это эффективные факторы роста, если вы используете начальный размер в качестве фиксированной суммы для добавления:

2.5
1.9
1.7
1.62
1.57
1.54
1.53
1.52
1.51
1.5
...
6
ответ дан 1 December 2019 в 19:07
поделиться

это не должно быть проблемой, поскольку контекст/маршруты Camel на рабе не будут запускаться, пока он не станет ведущим (когда блокировка файла хранилища сообщений будет снята ведущим)

-121--4460389-

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

-121--1886469-

Никто не может дать хороший совет, не зная что-то о распределении, среде выполнения, характеристиках выполнения и т.д.

Код, который работает намного важнее, чем высоко оптимизированный код... который находится в стадии разработки. Выберите какой-нибудь алгоритм - любой работоспособный алгоритм - и попробуйте его! Если это окажется неоптимальным, то измените стратегию. Постановка этого под контроль пользователя библиотеки часто не дает им никаких преимуществ. Но если у вас уже есть какая-то схема опций, то добавление ее может быть полезным, если только вы не нажмете на хороший алгоритм (и n ^ 1.5 является довольно хорошим).


Также использование функции с именем write в C (не C++) конфликтует с < io.h > и < stdio.h >. Это нормально, если ничто не использует их, но также будет трудно добавить их позже. Лучше всего использовать более описательное имя.

1
ответ дан 1 December 2019 в 19:07
поделиться
  • Удвойте размер до порогового значения (~ 100 МБ?), А затем снизьте экспоненциальный рост до 1,5, .., 1,3
  • Другой вариант - сделать размер буфера по умолчанию настраивается во время выполнения.
2
ответ дан 1 December 2019 в 19:07
поделиться

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

3
ответ дан 1 December 2019 в 19:07
поделиться

Ответ, как всегда, "зависит".

Идея экспоненциального роста, т. Е. Выделения нового буфера, который в x раз превышает текущий размер, заключается в том, что, поскольку вам требуется больше буфера, вам понадобится больше буфера, и есть вероятность, что вам понадобится гораздо больше буфера, чем небольшой фиксированный прирост обеспечивает.

Итак, если у вас есть 8-байтовый буфер и вам нужно больше выделения дополнительных 8 байтов, это нормально, тогда выделение дополнительных 16 байтов, вероятно, будет хорошей идеей - кому-то с 16-байтовым буфером вряд ли потребуется дополнительный 1 байт. И если они это сделают, все, что происходит, - вы тратите немного памяти.

Я думал, что лучший фактор роста - 2, то есть удвойте буфер, но если Кениг / Саттер говорят, что 1,5 является оптимальным, то я с ними согласен. Тем не менее, вы можете настроить скорость роста после получения некоторой статистики использования.

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

2
ответ дан 1 December 2019 в 19:07
поделиться

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

Пока вы работаете на настольном компьютере какого-либо типа, вы можете рассчитывать на практически неограниченный объем памяти, поэтому время, вероятно, является правильной стороной этого компромисса. Для жестких систем реального времени вы, вероятно, захотите найти способ избежать копирования вообще - на ум приходит связный список.

2
ответ дан 1 December 2019 в 19:07
поделиться

Попробуйте сделать следующее (только предположение) в нижней части CSS:

@media print {
    iframe {
       overflow: visible;
    }
}
-121--4501480-

Если вы уже используете Boost, то самый простой подход - использовать boost:: next .

for(std::list<int>::iterator i = l.begin(); i != l.end(); ++i)
    for(std::list<int>::iterator j = boost::next(i); j != l.end(); ++j)
-121--3787264-

Для этого конкретного случая можно изменить API, потребовав от вызывающего абонента выделения памяти для каждого блока, а затем запоминания блоков вместо копирования данных.

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

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

Недостаток состоит в том, что вызывающий абонент должен динамически распределять каждый блок. Чтобы обойти это, можно выделить память для каждого блока и запомнить их, а не хранить один большой буфер, размер которого изменяется при заполнении. Таким образом, вы будете копировать данные дважды (один раз в выделенный фрагмент, другой раз в результирующую последовательность), но не более. Если вам придется изменить размер несколько раз, вы можете получить более двух копий.

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

1
ответ дан 1 December 2019 в 19:07
поделиться

Стратегия экспоненциального роста используется во всем STL и, похоже, работает нормально. Я бы посоветовал придерживаться этой стратегии, по крайней мере, пока вы не найдете конкретный случай, когда она не будет работать.

8
ответ дан 1 December 2019 в 19:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: