Стек, статичный, и "куча" в C++

У меня был аналогичный вариант использования. Я разрешил его на Java, написав два пользовательских класса, реализующих MultipleTextOutputFormat и RecordWriter.

Мой вход был JavaPairRDD<String, List<String>>, и я хотел сохранить его в файле с именем по его ключу со всеми строками, содержащимися в его значении.

Вот код для моего MultipleTextOutputFormat реализация

class RDDMultipleTextOutputFormat<K, V> extends MultipleTextOutputFormat<K, V> {

    @Override
    protected String generateFileNameForKeyValue(K key, V value, String name) {
        return key.toString(); //The return will be used as file name
    }

    /** The following 4 functions are only for visibility purposes                 
    (they are used in the class MyRecordWriter) **/
    protected String generateLeafFileName(String name) {
        return super.generateLeafFileName(name);
    }

    protected V generateActualValue(K key, V value) {
        return super.generateActualValue(key, value);
    }

    protected String getInputFileBasedOutputFileName(JobConf job,     String name) {
        return super.getInputFileBasedOutputFileName(job, name);
        }

    protected RecordWriter<K, V> getBaseRecordWriter(FileSystem fs, JobConf job, String name, Progressable arg3) throws IOException {
        return super.getBaseRecordWriter(fs, job, name, arg3);
    }

    /** Use my custom RecordWriter **/
    @Override
    RecordWriter<K, V> getRecordWriter(final FileSystem fs, final JobConf job, String name, final Progressable arg3) throws IOException {
    final String myName = this.generateLeafFileName(name);
        return new MyRecordWriter<K, V>(this, fs, job, arg3, myName);
    }
} 

Вот код для моей реализации RecordWriter.

class MyRecordWriter<K, V> implements RecordWriter<K, V> {

    private RDDMultipleTextOutputFormat<K, V> rddMultipleTextOutputFormat;
    private final FileSystem fs;
    private final JobConf job;
    private final Progressable arg3;
    private String myName;

    TreeMap<String, RecordWriter<K, V>> recordWriters = new TreeMap();

    MyRecordWriter(RDDMultipleTextOutputFormat<K, V> rddMultipleTextOutputFormat, FileSystem fs, JobConf job, Progressable arg3, String myName) {
        this.rddMultipleTextOutputFormat = rddMultipleTextOutputFormat;
        this.fs = fs;
        this.job = job;
        this.arg3 = arg3;
        this.myName = myName;
    }

    @Override
    void write(K key, V value) throws IOException {
        String keyBasedPath = rddMultipleTextOutputFormat.generateFileNameForKeyValue(key, value, myName);
        String finalPath = rddMultipleTextOutputFormat.getInputFileBasedOutputFileName(job, keyBasedPath);
        Object actualValue = rddMultipleTextOutputFormat.generateActualValue(key, value);
        RecordWriter rw = this.recordWriters.get(finalPath);
        if(rw == null) {
            rw = rddMultipleTextOutputFormat.getBaseRecordWriter(fs, job, finalPath, arg3);
            this.recordWriters.put(finalPath, rw);
        }
        List<String> lines = (List<String>) actualValue;
        for (String line : lines) {
            rw.write(null, line);
        }
    }

    @Override
    void close(Reporter reporter) throws IOException {
        Iterator keys = this.recordWriters.keySet().iterator();

        while(keys.hasNext()) {
            RecordWriter rw = (RecordWriter)this.recordWriters.get(keys.next());
            rw.close(reporter);
        }

        this.recordWriters.clear();
    }
}

Большая часть кода точно такая же, как в FileOutputFormat. Единственное отличие состоит в том, что несколько строк

List<String> lines = (List<String>) actualValue;
for (String line : lines) {
    rw.write(null, line);
}

Эти строки позволили мне написать каждую строку моего ввода List<String> в файле. Первый аргумент функции write установлен на null, чтобы избежать нажатия клавиши на каждой строке.

Чтобы закончить, мне нужно только сделать этот вызов, чтобы записать мои файлы

javaPairRDD.saveAsHadoopFile(path, String.class, List.class, RDDMultipleTextOutputFormat.class);
157
задан user2864740 15 February 2014 в 09:22
поделиться

7 ответов

А подобный вопрос задали, но это не спрашивало о помехах.

Сводка того, каковы статичный, "куча" и стековая память:

  • статическая переменная А является в основном глобальной переменной, даже если Вы не можете получить доступ к ней глобально. Обычно существует адрес для него, который находится в самом исполняемом файле. Существует только одна копия для всей программы. Неважно, сколько раз Вы входите в вызов функции (или класс) (и в сколько потоков!) переменная относится к той же ячейке памяти.

  • "куча" является набором памяти, которая может использоваться динамично. Если Вы захотите 4 КБ для объекта тогда, то динамическое средство выделения просмотрит свой список свободного пространства в "куче", выберет блок 4 КБ и даст его Вам. Обычно средство выделения динамической памяти (malloc, новый, и c.) запускается в конце памяти и работ назад.

  • Объяснение, как стек растет и уменьшения, немного выходит за рамки этого ответа, но будьте достаточны, чтобы сказать, что Вы всегда добавляете и удаляете из конца только. Стеки обычно запускаются высоко и углубляются для понижения адресов. У Вас заканчивается память, когда стек встречает динамическое средство выделения где-нибудь в середине (но обратитесь к медосмотру по сравнению с виртуальной памятью и фрагментацией). Несколько потоков потребуют нескольких стеков (процесс обычно резервирует минимальный размер для стека).

, Когда Вы хотели бы использовать каждого:

  • Statics/globals полезны для памяти, что Вы знаете, что Вам всегда будет нужно, и Вы знаете, что никогда не хотите освобождать. (Между прочим, встроенные среды могут считаться наличием только статического ЗУ..., стек и "куча" являются частью известного адресного пространства, совместно использованного третьим типом памяти: код программы. Программы будут часто делать динамическое выделение из своего статического ЗУ, когда им будут нужны вещи как связанные списки. Но независимо, само статическое ЗУ (буфер) самостоятельно "не выделяется", а скорее другие объекты выделяются из памяти, сохраненной буфером с этой целью. Можно выполнить, это не встроило также, и консольные игры будут часто сторониться созданного в механизмах динамической памяти в пользу трудного управления процессом выделения при помощи буферов предварительно установленных размеров для всех выделений.)

  • переменные Стека полезны для того, когда Вы знаете, что, пока функция находится в объеме (на стеке где-нибудь), Вы захотите, чтобы переменные остались. Стеки хороши для переменных, в которых Вы нуждаетесь для кода, где они расположены, но который не необходим вне того кода. Они также действительно хороши для того, когда Вы получаете доступ к ресурсу, как файл, и хотите, чтобы ресурс автоматически ушел, при отъезде того кода.

  • выделения "кучи" (динамично выделенная память) полезны, когда Вы хотите быть более гибкими, чем вышеупомянутое. Часто, функция вызвана для ответа на событие (пользователь нажимает кнопку "create box"). Надлежащий ответ может потребовать выделения нового объекта (новый Объект поля), который должен слоняться поблизости еще долго после того, как из функции выходят, таким образом, это не может быть на стеке. Но Вы не знаете, сколько полей Вы хотели бы в начале программы, таким образом, это не могут быть помехи.

Сборка "мусора"

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

Сборка "мусора" является замечательным механизмом для того, когда производительность не является огромной проблемой. Я слышу, что GCs являются улучшением и более сложный, но факт, Вы можете быть вынуждены принять потерю производительности (в зависимости от варианта использования). И если Вы ленивы, это все еще не может работать правильно. В лучшие времена Сборщики "мусора" понимают, что Ваша память уходит, когда это понимает, что больше нет ссылок на него (см. подсчет ссылок ). Но, если у Вас будет объект, который относится к себе (возможно путем обращения к другому объекту, который вернулся), затем один только подсчет ссылок не укажет, что память может быть удалена. В этом случае GC должен посмотреть на весь ссылочный суп и фигуру, если существуют какие-либо острова, которые только упомянуты собой. Бесцеремонно, я предположил бы, что, чтобы быть O (n^2) операция, но независимо от того, что это, это может стать плохим, если Вы вообще обеспокоены производительностью. (Редактирование: Martin B указывает , что это - O (n) для довольно эффективных алгоритмов. Это все еще O (n) слишком много, если Вы обеспокоены производительностью и можете освободить в постоянное время без сборки "мусора".)

Лично, когда я слышу, что люди говорят, что C++ не имеет сборки "мусора", мои теги ума, что как функция C++, но я нахожусь, вероятно, в меньшинстве. Вероятно, самой твердой вещью для людей узнать о программировании в C и C++ являются указатели и как правильно обработать их динамические выделения памяти. Некоторые другие языки, как Python, были бы ужасны без GC, таким образом, я думаю, что это сводится к тому, что Вы хотите из языка. Если Вы хотите надежную производительность, то C++ без сборки "мусора" является единственной вещью эта сторона Фортрана, о котором я могу думать. Если Вы хотите простоту использования и учебные колеса (для сохранения Вас от катастрофического отказа, не требуя, чтобы Вы изучили "надлежащее" управление памятью), выберите что-то с GC. Даже если Вы будете знать, как управлять памятью хорошо, она сохранит Вас время, которое можно провести оптимизацию другого кода. Действительно нет большой части потери производительности больше, но если Вам действительно нужна надежная производительность (и способность знать точно, что продолжается, когда, под покрытиями) тогда я придерживался бы C++. Существует причина, что каждый главный игровой механизм, о котором я когда-либо слышал, находится в C++ (если не C или блок). Python, и др. хорошо для сценариев, но не основного игрового механизма.

213
ответ дан Community 23 November 2019 в 21:45
поделиться

Следующее - конечно, все не совсем точное. Возьмите его с мелкой частицей соли при чтении его:)

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

<час>

продолжительность Автоматического хранения

Вы используете продолжительность автоматического хранения для [1 128] недолгий и маленький данные, которые необходимы [только 1 130] локально в некотором блоке:

if(some condition) {
    int a[3]; // array a has automatic storage duration
    fill_it(a);
    print_it(a);
}

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

<час>

Статическая продолжительность хранения

Вы используете статическую продолжительность хранения для свободных переменных, к которым могли бы получить доступ любые все случаи кода, если их объем позволяет такое использование (объем пространства имен), и для локальных переменных, которые должны расширить их время жизни через выход их объема (локальный объем), и для членских переменных, которые должны быть совместно использованы всеми объектами их класса (объем класса). Их время жизни зависит от объема, в котором они находятся. Они могут иметь объем пространства имен и локальный объем и объем класса . То, что верно о них обоих, как только их жизнь начинается, пожизненные концы в [1 134] конец программы . Вот два примера:

// static storage duration. in global namespace scope
string globalA; 
int main() {
    foo();
    foo();
}

void foo() {
    // static storage duration. in local scope
    static string localA;
    localA += "ab"
    cout << localA;
}

печать программы ababab, потому что localA не уничтожается на выход ее блока. Можно сказать, что возражает, что имеют локальный объем, начинают время жизни , когда управление достигает их определения . Для localA, это происходит, когда тело функции вводится. Для объектов в объеме пространства имен время жизни начинается в [1 136] запуск программы . То же верно для статических объектов объема класса:

class A {
    static string classScopeA;
};

string A::classScopeA;

A a, b; &a.classScopeA == &b.classScopeA == &A::classScopeA;

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

<час>

продолжительность Динамической памяти

прошлая продолжительность хранения является динамичной. Вы используете его, если Вы хотите иметь объекты, живые на другом острове, и Вы хотите поместить указатели вокруг той ссылки их. Вы также используете их, если Ваши объекты большие , и если Вы хотите создать массивы размера, только известного в [1 138] время выполнения . Из-за этой гибкости объекты, имеющие продолжительность динамической памяти, являются сложными и замедляются для управления. Объекты, имеющие, что динамическая продолжительность начинает время жизни, когда соответствующее новый вызов оператора происходит:

int main() {
    // the object that s points to has dynamic storage 
    // duration
    string *s = new string;
    // pass a pointer pointing to the object around. 
    // the object itself isn't touched
    foo(s);
    delete s;
}

void foo(string *s) {
    cout << s->size();
}

Его время жизни заканчивается только, когда Вы звоните , удаляют для них. Если Вы забываете, что, те объекты никогда не заканчивают время жизни. И объекты класса, которые определяют пользователя, объявили, что конструктору не назовут их деструкторы. Объекты, имеющие продолжительность динамической памяти, требуют ручной обработки своего времени жизни и связанного ресурса памяти. Библиотеки существуют для упрощения использования их. Явная сборка "мусора" для [1 140] конкретные объекты могут быть установлены при помощи интеллектуального указателя:

int main() {
    shared_ptr<string> s(new string);
    foo(s);
}

void foo(shared_ptr<string> s) {
    cout << s->size();
}

Вы не должны заботиться о вызове, удалите: общий ptr делает это для Вас, если последний указатель, который ссылается на объект, выходит из объема. Сам общий ptr имеет продолжительность автоматического хранения. Так временем жизни автоматически управляют, позволяя ему проверить, должно ли это удалить указанный динамический объект в его деструкторе. Для shared_ptr ссылки см. документы повышения: http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/shared_ptr.htm

54
ответ дан bolov 23 November 2019 в 21:45
поделиться

Это было сказано продуманно, так же, как "короткий ответ":

  • статическая переменная (класс)
    время жизни = время выполнения программы (1)
    видимость = определенный модификаторами доступа (частными/защищать/общедоступными)

  • статическая переменная (глобальная область видимости)
    время жизни = время выполнения программы (1)
    видимость = единица компиляции, это инстанцируют в (2)

  • переменная "кучи"
    время жизни = определенный Вами (новый для удаления)
    видимость = определенный Вами (независимо от того, что Вы присваиваете указатель на)

  • переменная стека
    видимость = от объявления, пока из объема не выходят
    время жизни = от объявления до объявления, что из объема выходят

<час>

(1) более точно: от инициализации до deinitialization единицы компиляции (т.е. C / файл C++). Порядок инициализации единиц компиляции не определяется стандартом.

(2) Остерегайтесь: при инстанцировании статической переменной в заголовке каждая единица компиляции получает свою собственную копию.

38
ответ дан peterchen 23 November 2019 в 21:45
поделиться

, Каковы проблемы статических и стека?

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

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

я мог записать, что целое приложение без выделяет переменные в "куче"?

, Возможно, но не нетривиальное, нормальное, большое приложение (но так называемые "встроенные" программы мог бы быть записан без "кучи", с помощью подмножества C++).

, Что делает сборщик "мусора"?

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

Сборщики "мусора" не являются обычной функцией программирования на C++.

, Что Вы могли сделать управление памятью собой, что Вы не могли сделать использования этого сборщика "мусора"?

Изучают механизмы C++ для детерминированного освобождения памяти:

  • 'статичный': никогда не освобождал
  • 'стек': как только переменная "выходит из объема"
  • '"куча"': когда указатель удален (явно удаленный приложением, или неявно удалил в том или другой подпрограмме)
3
ответ дан ChrisW 23 November 2019 в 21:45
поделиться

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

Стек

Существенно быстрее для выделения. Это сделано в O (1), так как это выделяется при установке стекового фрейма, таким образом, это чрезвычайно свободно. Недостаток состоит в том, что, если у Вас заканчивается стековое пространство, Вы сняты с костей. Можно скорректировать размер стека, но IIRC у Вас есть ~2MB для игры с. Кроме того, как только Вы выходите из функции, все на стеке очищено. Таким образом, это может быть проблематично для обращения к нему позже. (Указатели для укладки выделенных объектов приводят к ошибкам.)

"куча"

Существенно медленнее для выделения. Но у Вас есть ГБ, чтобы играть с и указать на.

Сборщик "мусора"

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

5
ответ дан Chris Smith 23 November 2019 в 21:45
поделиться

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

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

1
ответ дан Rob Elsner 23 November 2019 в 21:45
поделиться

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

1
ответ дан kal 23 November 2019 в 21:45
поделиться
Другие вопросы по тегам:

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