Cross -Ошибка компиляции ядра Linux на bzImage

#!/usr/bin/perl

use strict;
use Set::Tiny;

my $max = shift;

# A set to hold all unique data_points:
my $all_dps = Set::Tiny->new();
# hash of sets. key is id, val is the set of data_points for that id:
my %ids = ();
# hash containing the number of data_points for each id:
my %counts = ();

# read the input file, parse into %ids
while(<>) {
  chomp;
  my ($id,$count,$dp) = split /\s+/,$_,3;            #/
  $ids{$id} = Set::Tiny->new(split /\s*,\s*/, $dp);  #/
  # The "#/" commentS exists solely to fix U&Ls perl syntax highlighting
  $counts{$id} = $count;

  $all_dps = $all_dps->union($ids{$id});
};

my $total_dps = keys %{ $all_dps };

# array to hold the list of output ids:
my @idlist=();
# set to hold the output data points:
my $data_points = Set::Tiny->new();
# count of output data points:
my $dpcount=0;

# stop when id list is = max. or when the count of output data points is equal
# to he total data points. or when there are no non-empty keys left.
while ((@idlist < $max) && ($dpcount < $total_dps) && (keys %ids > 0)) {

  # sort the counts hash by value.
  my @counts = ( sort { $counts{$b} <=> $counts{$a} } keys %counts );

  # add the sets from the id with the highest count to the output set.
  $data_points = $data_points->union($ids{$counts[0]});
  # and add that id to the output id list
  push @idlist, $counts[0];
  $dpcount = keys %{ $data_points };

  foreach (keys %ids) {
    my $diff = $ids{$_}->difference($data_points);

    if (keys %{$diff} == 0) {
      # delete ids with no difference from the current data_points set.
      delete $ids{$_};
      delete $counts{$_};
    } else {
      # add the intersection count as a decimal fraction so ids with more
      # dupes sort slightly higher.
      my $intersection = $ids{$_}->intersection2($data_points);
      $counts{$_} = (keys %{$diff}). ".".  (keys %{$intersection});
    };
  };
};

print join(",",@idlist).  "\t$dpcount/$total_dps\t".
  join(",",(sort keys %{ $data_points })).  "\n";

Этот скрипт сначала считывает весь входной файл и использует модуль Perl Set ::Tiny для построения «набора» (, т. е. хэша perl )и хэша, содержащего количество множества элементов для каждого идентификатора. Set::Tinyдоступен по ссылке CPAN выше, или он может быть уже упакован для вашего дистрибутива (, например. на Debian:sudo apt-get install libset-tiny-perl).

Затем он неоднократно пытается построить максимально большой выходной набор,:

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

По сути, это алгоритм, который вы назвали «неуклюжим» в своем комментарии. Я предпочитаю думать об этом как о «прямой -вперед» или «грубой -силе» :-)

.

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

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

Этот скрипт будет использовать много оперативной памяти. Если у вас 12 миллионов идентификаторов, он будет использовать примерно 12 MB * (the average id string length + the average data points length per id). Это может быть проблемой, если у вас менее 32 ГБ или, возможно, даже 64 ГБ оперативной памяти.

Если сценарий действительно превышает доступную вам оперативную память и вызывает перегрузку подкачки, вы можете использовать модуль MLDBM или один из модулей Tie::для хранения хэша %ids (и, возможно, % тоже считает хэш )в базе данных, а не в памяти. например. Свяжите ::DBI для использования базы данных sqlite, mysql или postgresql и т. д.

Использование MLDBMили модуля Tie::может не быть быстрее (, хотя и может, потому что он не будет перегружать ОЗУ и выполнять подкачку ), но )скрипт с гораздо меньшей вероятностью умереть до завершения из-за нехватки -из -состояния памяти,и b )это будет гораздо менее вредно для других процессов, запущенных в вашей системе (, которые в противном случае могут быть уничтожены из-за нехватки памяти ).

напр. добавьте следующее сразу после строк my %ids=()и my %counts=(), чтобы использовать файл Berkeley DB для %ids:

       use MLDBM qw(DB_File Storable);
       use Fcntl;
       my $id_db = tie %ids, 'MLDBM', './ids.db', O_CREAT|O_RDWR, 0640 or die $!;

и, возможно, это тоже, чтобы связать хэш %counts с базой данных DB:

       my $count_db = tie %counts, 'MLDBM', './counts.db', O_CREAT|O_RDWR, 0640 or die $!;

Пример вывода:

Я сохранил этот скрипт как ryan.pl, сделал его исполняемым с chmod +x ryan.plи запустил как:

$./ryan.pl 1 input.txt
id1     5/8   E1,E2,E3,E4,E5

$./ryan.pl 2 input.txt
id1,id3 7/8   E1,E2,E3,E4,E5,E6,E7

$./ryan.pl 3 input.txt
id1,id3,id5     8/8   E1,E2,E3,E4,E5,E6,E7,E8

Здесь на U&L плохо видно, но вывод разделен табуляцией -.


Некоторое предварительное тестирование (с входным файлом размером 145 МБ с 1 миллионом строк, каждая из которых содержит от 1 до 20 случайных словарных слов в качестве точек данных _), показало, что мое первоначальное предположение об использовании памяти было совершенно неверным.

Загрузка наборов в ОЗУ заняла около 23 минут (, и это только для загрузки файла данных без его обработки ), и потребляла 1 ГБ ОЗУ на моем Phenom II 1090T (с установленным 32 ГБ ОЗУ, но только около 8 ГБ. бесплатно ).

При использовании MLDBM загрузка файла данных заняла около 21 минуты. Он создал файл ids.dbразмером 323 МБ и файл counts.dbразмером 78 МБ. При этом он использовал постоянные 9,3 МБ ОЗУ.

Итак, я предполагаю, что размер вашего файла данных как минимум в 10 -20 раз больше, поэтому вряд ли он поместится в ОЗУ. Используйте MLDBM, желательно на твердотельном накопителе NVME для лучшей производительности.


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

Это как минимум в два раза быстрее, чем предыдущая версия. Потребовалось всего 15 минут, чтобы не только прочитать мой тестовый файл размером 145 МБ, но и обработать его и получить результат для 12 идентификаторов -. Лучшее, что я мог получить с другими попытками оптимизации, составило около 33 минут.

Это, ИМО,по-прежнему совершенно не подходит для чрезвычайно больших наборов данных, таких как упомянутый вами файл размером 104 ГБ.

Если вы все же хотите попробовать, я бы порекомендовал разделить его на два скрипта. Один для заполнения файлов.db (все до и включая цикл while (<>)), а второй скрипт (все до этого цикла while (<>), но без операторов unlink, конечно, а затем почти все после него )для работы с КОПИЕЙ файлов.db.

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

(копии необходимы, поскольку сценарий изменяет и удаляет записи в хэшах %ids и %counts по мере обработки данных. работа с копией позволяет быстро вернуть файлы.db к начальным условиям)

#!/usr/bin/perl

use strict;
use Set::Tiny;

# The first arg is the maximum number of identifiers we want.
# Any remaining args (and stdin) are input.
my $max = shift;

# hash of sets. key is id, val is the set of data_points for that id:
my %ids = ();

# hash containing the number of data_points for each id:
my %counts = ();

# The following two arrays exist to minimise memory usage, so that datapoints
# which appear in multiple IDs are stored in %id by reference rather than
# value.
#
# Array containing each datapoint indexed numerically
my @dp=();
# Hash containing each datapoint indexed by value
my %seen=();

use BerkeleyDB ;
use MLDBM qw(BerkeleyDB::Btree Storable);

# delete the.db files
unlink './ids.db';
unlink './counts.db';
unlink './seen.db';
unlink './dp.db';

# use MLDBM for the %ids hash - we need to serialise the Set::Tiny
# data structures.
tie %ids,    'MLDBM', -Filename => 'ids.db',    -Flags => DB_CREATE or die "Cannot open database 'ids.db': $!\n";

# It's faster to use BerkeleyDB directly for the other arrays (they
# contain scalar values, so there is no need for serialisation)
tie %counts, 'BerkeleyDB::Btree', -Filename => 'counts.db', -Flags => DB_CREATE or die "Cannot open database 'counts.db': $!\n";
tie %seen,   'BerkeleyDB::Btree', -Filename => 'seen.db',   -Flags => DB_CREATE or die "Cannot open database 'seen.db': $!\n";
tie @dp,     'BerkeleyDB::Recno', -Filename => 'dp.db',     -Flags => DB_CREATE or die "Cannot open database 'dp.db': $!\n";

my $total_dps=0;
# read the input file, parse into %ids
while(<>) {
  chomp;
  # split input line on spaces with max of 3 fields.
  my ($id,$count,$data) = split(/\s+/,$_,3);   #/

  # split $data by commas
  my @data = split(/\s*,\s*/, $data);          #/
  my $data_count = @data;
  my @data_by_idx = ();

  # convert @data to  @data_by_idx
  for (0..$#data) {
    if (!defined($seen{$data[$_]})) {
      # We haven't seen this datapoint before, so add it to both @dp
      # and %seen.
      $dp[++$total_dps] = $data[$_];
      $seen{$data[$_]}=$total_dps;
    };
    # add the datapoint's index number to @data_by_idx
    push @data_by_idx, $seen{$data[$_]};
  };
  $ids{$id} = Set::Tiny->new(@data_by_idx);

  $counts{$id} = $count;
};

# array to hold the list of output ids:
my @idlist=();
# set to hold the output data points:
my $data_points = Set::Tiny->new();
# count of output data points:
my $dpcount=0;

my $biggest_id='unknown';
my $biggest_count=0;

# stop when id list is = max. or when the count of output data points
# is equal to he total data points. or when there are no non-empty
# keys left.
while ((@idlist < $max) && ($dpcount < $total_dps) && (keys %ids > 0)) {

  # find the id with the most data points without using sort().
  if ($biggest_id eq 'unknown') {
    foreach (keys %counts) {
      if ($counts{$_} > $biggest_count) {
        $biggest_count = $counts{$_};
        $biggest_id = $_;
      };
    };
  };

  # add the sets from the id with the highest count to the output set.
  $data_points = $data_points->union($ids{$biggest_id});
  # and add that id to the output id list
  push @idlist, $biggest_id;
  $dpcount = keys %{ $data_points };

  $biggest_count=0;

  foreach (keys %ids) {
    my $diff = $ids{$_}->difference($data_points);

    if (keys %{$diff} == 0) {
      # delete ids with no difference from the current data_points set.
      delete $ids{$_};
      delete $counts{$_};
    } else {
      # add the intersection count as a decimal fraction so ids with more
      # dupes sort slightly higher.
      my $intersection = $ids{$_}->intersection2($data_points);
      $counts{$_} = (keys %{$diff}). ".".  (keys %{$intersection});
      # find the new id with the most data points.
      if ($counts{$_} > $biggest_count) {
        $biggest_count = $counts{$_};
        $biggest_id = $_;
      };
    };
  };
};

print join(",",@idlist).  "\t$dpcount/$total_dps\t";
print join (",", (map $dp[$_], keys %{ $data_points })), "\n";

Что касается вашего другого вопроса в комментарии (, то есть как разделить данные для многоядерной -обработки в кластере ), я понятия не имею.

Я не думаю, что это задача, поддающаяся осколкам данных, параллельной обработке осколков и последующему объединению результатов, потому что НАСТОЯТЕЛЬНО любому такому процессу потребуется доступ ко всему всему набору данных для создания любого осмысленный вывод.

Эта задача связана с вводом-выводом -, а не с процессором -в любом случае -это не сложно или «дорого» в вычислительном отношении, просто требуется много времени (и памяти )для читать и обрабатывать огромный набор данных.

Не верь мне на слово, мысль. Я почти ничего не знаю о ваших данных или о том, что вы пытаетесь сделать.Кто-то, кто a )лучше понимает ваш набор данных и b )знает, что вы пытаетесь извлечь из него, может эффективно сегментировать ваши данные и по-прежнему иметь возможность комбинировать наборы результатов.

1
26.02.2021, 00:11
1 ответ

Судя по всему, правильная процедура компиляции ядра Linux отличается для каждой платформы:

# on 32-bit x86
make bzImage

# on 64-bit x86
make zImage

# on 64-bit ARM
make Image

# search for compiled linux kernel in current directory
find./ -iname "*Image"
1
18.03.2021, 22:28

Теги

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