LINUX.ORG.RU

[ФП] Примеры работы с БОЛЬШИМИ файлами


4

0

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

Предистория такова, мы обрабатываем изображения с телескопов, там счёт идёт на сотни мегапикселей, и глубина пикселя 32 бита. Так что типичное изображение ~ два с половиной гигабайта, для этих целей специально собраны счётные узлы с 4 Гб RAM. Это чтобы изображение поместилось целиком в память, и оставалось на промежуточные буферы для накопления результатов. Естественно, все рассчёты написаны на Си и С++, работает быстро, памети хватает. Но код некрасивый, много повторяющихся конструкций и т.п. Народ в основном закостенелый из старшего поколения, ничего кроме Си и фортрана не знают, а я хочу попробывать более современные языки.

Так что буду благодарен за примеры чтения массивов для Haskell и особенно Scheme. И чтобы можно было посмотреть, сколько памяти реально израсходовано. Спасибо!

> работает быстро, памети хватает. Но код некрасивый, много повторяющихся конструкций и т.п.

google: рефакторинг С++

ahonimous ()
Ответ на: комментарий от ahonimous

google: рефакторинг С++

Ты не понимаешь, там никакой рефакторинг не справиться, потому что на грани возможностей языка. Смотрите:

#define FORC(cnt) for (c=0; c < cnt; c++)
#define FORC3 FORC(3)
#define FORC4 FORC(4)
#define FORCC FORC(colors)

void CLASS blend_highlights()
{
  int clip=INT_MAX, row, col, c, i, j;
  static const float trans[2][4][4] =
  { { { 1,1,1 }, { 1.7320508,-1.7320508,0 }, { -1,-1,2 } },
    { { 1,1,1,1 }, { 1,-1,1,-1 }, { 1,1,-1,-1 }, { 1,-1,-1,1 } } };
  static const float itrans[2][4][4] =
  { { { 1,0.8660254,-0.5 }, { 1,-0.8660254,-0.5 }, { 1,0,1 } },
    { { 1,1,1,1 }, { 1,-1,1,-1 }, { 1,1,-1,-1 }, { 1,-1,-1,1 } } };
  float cam[2][4], lab[2][4], sum[2], chratio;

  if ((unsigned) (colors-3) > 1) return;
  if (verbose) fprintf (stderr,_("Blending highlights...\n"));
  FORCC if (clip > (i = 65535*pre_mul[c])) clip = i;
  for (row=0; row < height; row++)
    for (col=0; col < width; col++) {
      FORCC if (image[row*width+col][c] > clip) break;
      if (c == colors) continue;
      FORCC {
	cam[0][c] = image[row*width+col][c];
	cam[1][c] = MIN(cam[0][c],clip);
      }
      for (i=0; i < 2; i++) {
	FORCC for (lab[i][c]=j=0; j < colors; j++)
	  lab[i][c] += trans[colors-3][c][j] * cam[i][j];
	for (sum[i]=0,c=1; c < colors; c++)
	  sum[i] += SQR(lab[i][c]);
      }
      chratio = sqrt(sum[1]/sum[0]);
      for (c=1; c < colors; c++)
	lab[0][c] *= chratio;
      FORCC for (cam[0][c]=j=0; j < colors; j++)
	cam[0][c] += itrans[colors-3][c][j] * lab[0][j];
      FORCC image[row*width+col][c] = cam[0][c] / colors;
    }
}

Код привёл из dcraw (http://www.cybercom.net/~dcoffin/dcraw/dcraw.c), но у нас по-сути всё тоже самое, поскольку тот же BAYER паттерн. И повсюду это уродское FORCC

Ignatik ()
Ответ на: комментарий от Ignatik

Код, конечно, ужасный, но легко приводимый к нормальному виду. Это, по крайней мере, будет лучше чем переписывать всё это на ФП и сделать его непонятным ещё большему кол-ву кодеров. Тем более, как я вижу, вы не особо то и разбираетесь с ФП, нп.

mikki ()
Ответ на: комментарий от Ignatik
#define FORC(cnt) for (c=0; c < cnt; c++) 
#define FORC3 FORC(3) 
#define FORC4 FORC(4) 
#define FORCC FORC(colors)

Какой ужас? Думаю проблема не в языке.

pathfinder ★★★ ()
Ответ на: комментарий от Ignatik

Надо просто побить этот поток сознания на несколько частей и распихать по функциям с осмысленными именами.

pathfinder ★★★ ()
Ответ на: комментарий от Ignatik
for (row=0; row < height; row++) 
    for (col=0; col < width; col++) {
...
image[row*width+col][c]
...
image[row*width+col][c]
...
image[row*width+col][c]
...
}

Ннда... Не бывает плохих программистов. Бывают плохие оптимизирующие компиляторы.

pathfinder ★★★ ()

>Вопрос такой, как в ФП-языке считать в память огромный файл как двумерный массив, и чтобы он а) занимал в памяти столько же места сколько на диске б) доступ к элементам был быстрый (О(1))?

изображения с телескопов, там счёт идёт на сотни мегапикселей, и глубина пикселя 32 бита.


Т.е. у нас есть файл, и из него надо получить двумерный массив 32битных беззнаковых целых? Если четче обрисуешь задачу, покажу пример на CL.

Гомогенные массивы(в которых доступ к элементу - O(1)), в т.ч. многомерные, в которых данные представлены целым куском памяти, в ф.я. есть. Работа с ними, правда, удобна в разных языках в разной степени(в хаскеле, я так понимаю, в меньшей, в scheme, и, особенно, CL - в большей). Бинарный ввод/вывод в них тоже есть; его удобство, правда, опять же, различается(тут опять CL в лидерах).


Ты не понимаешь, там никакой рефакторинг не справиться, потому что на грани возможностей языка. Смотрите:

Ну, тут, в принципе, нормальная маркосистема поможет.

Love5an ()
Ответ на: комментарий от Ignatik

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

И повсюду это уродское FORCC


тут очень много циклов - и вроде как все обоснованы, так что либо разбивать функцию на несколько, либо соглашаться с автором и таким его способом сократить код

П.С насчет ФЯП - если уж ты решил решил переписать все на ФЯП, потому-что часть кода некрасива, то для начала возьми тот же кусок кода, что ты привел, или аналогичный из вашего проекта и попробуй:

1. просто оформить как тебе нравится, таки сделать рефакторинг
2. переписать на ФЯП
3. сравнить читабельность - т.к. как не факт, что получится лучше
4. сравнить скорость работы
5. учесть, что любые изменения существующего кода можно делать постепенно и ничего не ломая, а переписывание - это неопределенные сроки и неопределенный результат, который возможно окажется хуже того, что было ( как по скорости - так точно )

ahonimous ()
Ответ на: комментарий от Love5an

> Дебилов, которые посоветуют оставить С++ - не слушай.

только дебил посоветует переписать большой работающий проект с нуля

ahonimous ()
Ответ на: комментарий от Love5an

> У автора не настолько большой, чтобы его было лень переписывать

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

ahonimous ()

>Но код некрасивый, много повторяющихся конструкций и т.п.

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

linuxfan ()
Ответ на: комментарий от ahonimous

тут очень много циклов - и вроде как все обоснованы, так что либо разбивать функцию на несколько, либо соглашаться с автором и таким его способом сократить код

Соглашаться с автором ни в коем случае нельзя.

Там по ссылке 250кб в одном, повторю ещё раз в ОДНОМ, файле феерического быдлокода написанного на мерзком «Pure C» с активным использованием глобальных переменных. Видимо автор считает, что многопотоковые приложения не нужны.

pathfinder ★★★ ()
Ответ на: комментарий от pathfinder

> Там по ссылке 250кб в одном, повторю ещё раз в ОДНОМ, файле

с активным использованием глобальных переменных


жестко

написанного на мерзком «Pure C»


тут не согласен - С язык богов :)

ahonimous ()

Не надо в память читать, надо просто использовать mmap.

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

Чтобы получить еще более корявый код? У тебя задача сама просится, чтобы её на Си написали.

Reset ★★★★★ ()
Ответ на: комментарий от pathfinder

>Там по ссылке 250кб в одном, повторю ещё раз в ОДНОМ, файле феерического быдлокода написанного на мерзком «Pure C» с активным использованием глобальных переменных.

А ты думал, что все задачи в этом мире изящно решаются на жавоскрипте с использованием жейквери или вызовом библиотечных функций на иных ЯП?

linuxfan ()
Ответ на: комментарий от linuxfan

> А ты думал, что все задачи в этом мире изящно решаются на жавоскрипте с использованием жейквери или вызовом библиотечных функций на иных ЯП?

С не обязывает хранить весь код в одном файле :)

ahonimous ()
Ответ на: комментарий от linuxfan

Есть в пользу этого хоть один разумный аргумент?

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

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от pathfinder

И потому, что он мерзкий «Pure C», его нужно переписать (все 250кб) ещё более мерзко на ЯП с шаблонными мыслями в глове, незная даже как. Отличный план.

mikki ()
Ответ на: комментарий от linuxfan

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

Reset ★★★★★ ()
Ответ на: комментарий от Reset

> Если файл огромный, то очевидно, что он используется сразу не весь

типичное изображение ~ два с половиной гигабайта, для этих целей специально собраны счётные узлы с 4 Гб RAM. Это чтобы изображение поместилось целиком в память, и оставалось на промежуточные буферы для накопления результатов. Естественно, все рассчёты написаны на Си и С++, работает быстро, памети хватает (c)

ahonimous ()
Ответ на: комментарий от linuxfan

>А ты думал, что все задачи в этом мире изящно решаются на жавоскрипте с использованием жейквери или вызовом библиотечных функций на иных ЯП?

Ну при чём тут жабаскрипт? Тебя немного занесло.

Уверен, что данную задачу вполне можно изящно решить на плюсах или на чистом си. А вот если ТС начнет это делать на Хаскеле, то ИМХО он станет хорошим преемником старой школы FORCC-подхода. Правда FORCC будет уже на Хаскеле.

pathfinder ★★★ ()
Ответ на: комментарий от mikki

>И потому, что он мерзкий «Pure C»...

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

pathfinder ★★★ ()
Ответ на: комментарий от ahonimous

Ну и? С mmap'ом оно само засосется в эти 4 гига по мере необходимости, а если приложение сожрет чуть больше памяти, то неиспользуемые куски файла просто выгрузятся из памяти, а не уйдут в своп. А с твоим подходом если что пойдет не так, что уйдем в своп и машина встанет колом.

Reset ★★★★★ ()
Ответ на: комментарий от Reset

> А с твоим подходом

o_O

уйдем в своп и машина встанет колом


а еще, если активно процессор нагружать - он может сгореть, надо специально паузы делать в работе программы, ТС явно сказал - ОЗУ ставят сколько надо, на P1 никто не запускает программу

ahonimous ()
Ответ на: комментарий от Reset

>Если файл огромный, то очевидно, что он используется сразу не весь.

Если обращения не совсем случайные (в примере топикстартера мне видится что-то похожее на последовательное чтение), лучше последовательно читать файл в «окно» в памяти. Гарантированно быстрее mmap'а с его page faults.

linuxfan ()
Ответ на: комментарий от pathfinder

>Ну при чём тут жабаскрипт? Тебя немного занесло.

Не занесло. Просто есть задачи, решение которых (особенно эффективное) выглядит действительно некрасиво. Обработка изображений как раз такая «некрасивая» область.

linuxfan ()
Ответ на: комментарий от ahonimous

>> Если файл огромный, то очевидно, что он используется сразу не весь

типичное изображение ~ два с половиной гигабайта, для этих целей специально собраны счётные узлы с 4 Гб RAM. ... памети хватает (c)

Зря ты так. Думаю у этих товарищей поддержки 64-бит нет. Достаточно, что бы прогресс в области цифровых телескопов слегка шагнул вперед, и... Большой полярный лис придёт к ним. И вместе с ним прийдет осознание того, что надо весь код в любом случае придется переписывать. Либо под 64-бита, либо под экономное использование памяти.

pathfinder ★★★ ()
Ответ на: комментарий от linuxfan

>Не занесло. Просто есть задачи, решение которых (особенно эффективное) выглядит действительно некрасиво.

Ты хочешь сказать, что представленный выше код «эффективен»?

pathfinder ★★★ ()
Ответ на: комментарий от pathfinder

И вместе с ним прийдет осознание того, что надо весь код в любом случае придется переписывать. Либо под 64-бита, либо под экономное использование памяти.

Это точно. В спекл-интерферометрии за ночь можно получить около 2Тб данных, а их ведь еще и обработать надо! На мощной машинке все это добро считается всего лишь 3-5 дней. Естественно, оперативки надо хотя бы 64Гб.

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от pathfinder

>Ты хочешь сказать, что представленный выше код «эффективен»?

На первый взгляд никакой лажи там нет. А что тебе не понравилось?

linuxfan ()
Ответ на: комментарий от pathfinder

Тот код вообще лучше не смотреть: головная боль обеспечена! А уж разбирать его... Черт же ногу сломает!

Eddy_Em ☆☆☆☆☆ ()

Кстати, Ignatic, откройте для себя MIDAS, тогда и разбираться в этой куче кода не придется: накатаете небольшой скрипт, и пусть себе обсчитывают.

Да, а что этот код делает-то?

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от pathfinder

Ты хочешь сказать, что представленный выше код «эффективен»?

сходу видно, что

  for (row=0; row < height; row++) 
    for (col=0; col < width; col++) { 

заменяется на один цикл:

for( char* p = image; p < end ; p += colors )

image[row*width+col][c]
...
image[row*width+col][c]
...
image[row*width+col][c]

постоянно считается одно и тоже для базового смещения, что также уйдет

ahonimous ()
Ответ на: комментарий от ahonimous

>заменяется на один цикл

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

постоянно считается одно и тоже для базового смещения, что также уйдет

Это уйдет наверняка.

linuxfan ()
Ответ на: комментарий от ahonimous

>а что тут неэффективного? нет временной переменной - больше шансов все запихнуть в регистры

Сложение, СЛОЖЕНИЕ мне понравилось. Дааааа. Экономия регистров это сильный аргумент. На крайний случай можно использовать XOR.

pathfinder ★★★ ()
Ответ на: комментарий от pathfinder

> Сложение, СЛОЖЕНИЕ мне понравилось. Дааааа

да - я тоже удивился, что не xor, но это уже не вопрос эффективности

ahonimous ()
Ответ на: комментарий от pathfinder

> На пофапай на экономию регистров

спасибо - нам в школе тоже это показывали

ahonimous ()
Ответ на: комментарий от ahonimous

>а что тут неэффективного?

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

linuxfan ()
Ответ на: комментарий от pathfinder

XOR там использовать нельзя, т.к. SWAP() работает с разными типами (целые и с плав. точкой). Нужно отходить от ANSI C или делать как там, или для кадого типа свой SWAP

mikki ()
Ответ на: комментарий от linuxfan

Читать последовательно read'ом в фиксированный буфер? Медленнее будет, я проверял. Да еще и кода больше получится.

Reset ★★★★★ ()
Ответ на: комментарий от mikki

>XOR там использовать нельзя, т.к. SWAP() работает с разными типами (целые и с плав. точкой). Нужно отходить от ANSI C или делать как там, или для кадого типа свой SWAP

Интересно, а как оно SWAP(DBL_MAX,(DBL_MAX/10,0)) сделает?

Не надо было вообще эти дефайны делать. Да и вообще. Это мертвый код. Он абсолютно не дружествен к рефакторингу. Можно только лепить сверху на то, что есть.

pathfinder ★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.