LINUX.ORG.RU
 
siberean

Вау, джава не медленнее Си на обработке больших текстовых массивов


0

3

Только что портил некоторые тулзы, обрабатывающие генетическую информацию с ANSI C на джаву, строка-в-строку.
И был очень удивлён, что разница - в пределах ошибки.
Тулзы типа транспонируют матрицы, ну просто крутят данные, а данных могут быть многие гигабайты. Только одна строка - порядка миллиона аллелей, а их может быть много.
Дык вот, на Си, скажем, миллион рекордов обрабатывается в 1.5-1.8 секунд. На джаве - 2.5 секунд вместе с загрузкой виртуальной машины, но саму строку обрабатывает 1.8 секунд (колеблется в приделах 20% в моём ленивом семпле из 5 запусков). Т.е. когда количество строк будет очень большим - загрузкой VM можно пренебречь, а данные колошматят как Си (gcc -O2) и джава - с одинаковой скоростью.

Детали: маллоками не пользуюсь, всё делаю на стеке. В джаве тоже всё в стиле Си (немногие классы - как сишные структуры с публичным доступом - на частые операции, в основном - функции). Никакого юникода (его в тех форматах не предвидится), поэтому читаю асками, как intы и кастю в char под конец, только что нужно. Т.е. оптимизированнее некуда.

Этак можно на Си забить для подобных задач (я давно тестил одну и ту же большую экстракцию данных из базы - разницы не заметил, но там ясно: чистое ожидание IO, в базах на Си давно забил. Потом тестил хэши, работа с большимы хешами также в джаве оказалась даже быстрее). А тут такое. С большими локальными файлами и их обработке. Однако...


[#]  
spoilt

Вот и чудненько. Мне в целом Java нравится, но порой по оформлению она никуда не вписывается.

* ()
[#]  
robot12

Где тэг [жж] ???

***** ()
[#] Ответ на: комментарий от robot12 30.04.2011 9:12:42  
siberean

> Где тэг [жж] ???

Кстати, хотел на ЖЖ выложить статейку с замерами. Лень. Поэтому здесь, в толксах.

* ()
[#]  

> колеблется в приделах 20% в моём ленивом семпле из 5 запусков

> приделах

facepalm

***** ()
[#] Ответ на: комментарий от Obey-Kun 30.04.2011 9:46:29  
siberean

>> приделах

> facepalm


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

* ()
[#]  
andreyu

Все верно, и на си можно писать говнокод, который по скорости будет равен говнокоду на яве.

***** ()
[#] Ответ на: комментарий от andreyu 30.04.2011 10:13:55  
siberean

> Все верно, и на си можно писать говнокод, который по скорости будет равен говнокоду на яве.

скажу Вам, о гуру, спасибо - за советы по оптимизации например этого файла (только если действительно советы приведут к повышению перформанса):

http://ped2raw.svn.sourceforge.net/viewvc/ped2raw/ped2raw/main.c?revision=5&v...

на мой взгляд, писать более оптимально просто невозможно. Этот простой пример, есть гораздо более сложные, в других проектах - стейт-машина специально поддерживается для перформанса, где ничего никогда не читается 2 раза или ничего лишнего никогда не находится в памяти. Работает на моём Пентиуме 2/450Mhz/153М RAM 12-15 секунд (предыдущие цифры были даны для AMD64 3400, 1Гиг)

* ()
[#] Ответ на: комментарий от siberean 30.04.2011 10:24:11  
siberean

Тот же код на джаве:

http://aisconvert.svn.sourceforge.net/viewvc/aisconvert/aisconvert/src/org/ai...

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

я почему так озаботился перформансом Си - это потому что собрался переписывать более сложные алгоритмы на Си, например вот этот:

http://aisconvert.svn.sourceforge.net/viewvc/aisconvert/aisconvert/src/org/ai...

А тут такое... (что разницы на больших текстовых данных - в общем то и нет)...

* ()
[#] Ответ на: комментарий от siberean 30.04.2011 9:23:37  
memnek

> что не так со свингом?
он страшный и не мимикрирует под нужный тулкит

* ()
[#] Ответ на: комментарий от siberean 30.04.2011 10:24:11  
aho

> на мой взгляд, писать более оптимально просто невозможно.

ну да, при парсировании разделенного табами текста из файла использовать strtok, strcpy, fgets - это супер-пупер оптимально, ага

()
[#] Ответ на: комментарий от siberean 30.04.2011 10:36:38  
aho

> А тут такое... (что разницы на больших текстовых данных - в общем то и нет)...

а вы пишите на С как на С, а не как на Java - и все будет хоккей

()
[#]  
nu11

>Только что портил некоторые тулзы, обрабатывающие генетическую информацию с ANSI C на джаву
>портил


более точного описания и не подберешь :)

***** ()
[#]  
elverion

Она и на вычислениях не сильно сливает.

()
[#]  
nu11

а теперь 2 вопроса:
1) насколько хорошо распараллеливается алгоритм? Сколько потоков используется сейчас?
2) сколько памяти жрет старый и новый вариант на этих самых "гигабайтах данных"?

Код не видел, но судя по описанию, каждая строка данных обрабатывается абсолютно независимо. Т.е. имеем самую примитивную числодробильную задачу из возможных, что само по себе малоинтересно. И уж тем более не показатель для сравнения.

***** ()
[#]  
DNA_Seq

На си вообще обработка строк через жопу сделана. А что на числодробилках жаба идет вровень с си не новость. На http://shootout.alioth.debian.org/u32/benchmark.php?test=all&lang=java&lang2=gcc местами жаба в полтора раза быстрее си (особенно на всяких выравниваниях)

*** ()
[#] Ответ на: комментарий от nu11 30.04.2011 11:30:15  
DNA_Seq

>более точного описания и не подберешь :)

Те кто в теме поймут. Правда они обычно прекрасно в курсе что подобное лучше на перле писать

*** ()
[#] Ответ на: комментарий от aho 30.04.2011 11:02:53  
bga_

дико плюсую. fputc убил наповал

* ()
[#]  
iBliss

> Этак можно на Си забить для подобных задач

Теперь запусти в 2-3 параллельных процесса на яве и столько же на С и пересчитай ресурсы. В современных реализациях в итоге выполняется такой же скомпилированный код. Расплата идёт ресурсами.

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

* ()
[#]  
segfault

> Потом тестил хэши, работа с большимы хешами также в джаве оказалась даже быстрее).

В каком смысле "работа с хешами"??? Чем хеши отличаются от других данных?

* ()
[#]  
eveel

Если программа несложная, было бы интересно ещё посмотреть сравнение с производительностью языка D (компилятор на выбор: ldc или dmd, но лучше dmd). Писать на нём приятнее, чем на C и Java.

** ()
[#] Ответ на: комментарий от aho 30.04.2011 11:02:53  

> при парсировании разделенного табами текста из файла использовать strtok, strcpy, fgets - это супер-пупер оптимально, ага

А что не так?

***** ()
[#] Ответ на: комментарий от tailgunner 30.04.2011 14:48:55  
aho

> А что не так?

a) вычитка файла через getc - медленнее чем читать сразу в буфер ( fgets - это еще я случайно увидел, там getc везде )

б) просто вернуть указатель - это быстрее чем делать strcpy

()
[#] Ответ на: комментарий от siberean 30.04.2011 10:24:11  
AndreyKl

> на мой взгляд, писать более оптимально просто невозможно.

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

*# ()
[#] Ответ на: комментарий от aho 30.04.2011 14:57:27  

> a) вычитка файла через getc - медленнее чем читать сразу в буфер ( fgets - это еще я случайно увидел, там getc везде )

getc тоже читает из буфера.

> б) просто вернуть указатель - это быстрее чем делать strcpy

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

***** ()
[#] Ответ на: комментарий от eveel 30.04.2011 14:43:52  
vertexua

Таких языков чтобы приятнее было код писать еще куча. Но все не то, несерьезно как-то

*** ()
[#] Ответ на: комментарий от tailgunner 30.04.2011 15:24:45  
aho

> getc тоже читает из буфера.

тоже, вот только сделать инкремент указателю на char* "дешевле" чем вызвать функцию

> Может быть, но на горячем кэше вряд ли разница велика...


да - может всего-лишь в несколько раз будет быстрее

П.С. вот кстати про strtok:

http://paste.ubuntu.com/601284/

в три раза практически разница, это про "оптимизированнее некуда"

()
[#] Ответ на: комментарий от aho 30.04.2011 15:35:41  

> в три раза практически разница, это про "оптимизированнее некуда"

В коде 2 вызова strok, причем 1 из них вне цикла. Ты правда думаешь, что увеличение производительности strtok в 3 раза даст 3-кратный выигрыш в производительности? Да хоть заметный выигрыш?

***** ()
[#] Ответ на: комментарий от tailgunner 30.04.2011 15:48:46  
aho

> В коде 2 вызова strok, причем 1 из них вне цикла.

а за время работы программы - тоже будет один вызов?

> Ты правда думаешь, что увеличение производительности strtok в 3 раза даст 3-кратный выигрыш в производительности?


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

()
[#] Ответ на: комментарий от aho 30.04.2011 15:51:29  

>> В коде 2 вызова strok, причем 1 из них вне цикла.

> а за время работы программы - тоже будет один вызов?

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

> если программу в целом "подтянуть" - она и работать в целом будет быстрее

Я как раз в этом и сомневаюсь. По-моему, типичная IO-bound прога. Небось, еще и обязана уметь файлы в несколько гиг.

***** ()
[#] Ответ на: комментарий от tailgunner 30.04.2011 15:58:24  
aho

> И где там тратится время - без профайлера не скажешь.

согласен

> По-моему, типичная IO-bound прога. Небось, еще и обязана уметь файлы в несколько гиг.


тоже согласен, и задача программиста - ее такой и оставить, а не налепить миллиарды вызовов getc и т.д.

()
[#]  

Вполне возможно, что вы исследуете I/O системы, а не скорость реализации
на ЯП.

> Т.е. оптимизированнее некуда.


И что, даже mmap используется ? // ъ код не смотрел

*** ()
[#] Ответ на: комментарий от aho 30.04.2011 16:00:36  

>> По-моему, типичная IO-bound прога. Небось, еще и обязана уметь файлы в несколько гиг.

> тоже согласен, и задача программиста - ее такой и оставить, а не налепить миллиарды вызовов getc и т.д.

Задача прогера в таком случае - сделать программу без изъебов. Это значит - не писать свою буферизацию и токенизатор.

***** ()
[#] Ответ на: комментарий от tailgunner 30.04.2011 16:25:29  
aho

> Задача прогера в таком случае - сделать программу без изъебов. > Это значит - не писать свою буферизацию

хорошо, показываю на пальцах:

$ echo "1.c" && cat ./1.c && echo "2.c" && cat ./2.c && gcc -O2 ./1.c && time ./a.out && gcc -O2 ./2.c && time ./a.out 
1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	FILE* f = fopen( "/home/igor/koffice-2.3.1.tar.bz2", "rb" );

	int c = 0;
	int s = 0;

	while( !feof( f ) )
		s += (char) getc( f );

	fclose( f );

	return 0;
}

2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	FILE* f = fopen( "/home/igor/koffice-2.3.1.tar.bz2", "rb" );

	char* p = malloc( 200000000 );
	int   s = 0;

	char* end = p + fread( p, 1, 200000000, f );
	char* it = p;
	while( it < end ) s += *it++;
	
	free( p );

	return 0;
}


real	0m1.152s
user	0m1.132s
sys	0m0.016s

real	0m0.074s
user	0m0.000s
sys	0m0.072s

разница быдлокода "без изъебов" и "сложного" буфера хорошо видна?

()
[#] Ответ на: комментарий от aho 30.04.2011 16:50:32  
aho

да - смена порядка выполнения ни на что не влияет, если ты опять что-то про кэши начнешь думать

()
[#] Ответ на: комментарий от aho 30.04.2011 16:54:41  
aho

вот более правильный вариант:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUF_SIZE 1024 * 1024

int main()
{
	FILE* f = fopen( "/home/igor/koffice-2.3.1.tar.bz2", "rb" );

	char   p[ BUF_SIZE ];
	int    s = 0;
	size_t c;

	do
	{
		c = fread( p, 1, BUF_SIZE, f );

		char* end = p + c;
		char* it = p;
		while( it < end ) s += *it++;
	}
	while( c == BUF_SIZE );

	return 0;
}

выполняется уже за 0m0.021s

()
[#] Ответ на: комментарий от aho 30.04.2011 17:14:00  
ckotinko

унизил жабодроидов аки тузик грелку

* ()
[#] Ответ на: комментарий от aho 30.04.2011 16:50:32  

> разница быдлокода "без изъебов" и "сложного" буфера хорошо видна?

Нет :D Ты отмонтировал ФС между тестами? %)

***** ()
[#] Ответ на: комментарий от tailgunner 30.04.2011 17:44:11  
aho

> Нет :D Ты отмонтировал ФС между тестами? %)

там файл в 60Мб - читается около секунды на "свежей" ФС, код ""без изъебов" дольше на нем сумму считает, не говоря уж про пропарсить и обработать

()
[#] Ответ на: комментарий от aho 30.04.2011 17:14:00  
siberean

Большое спасибо всем. Очень познавательно.
С буферами я не заморачивался, а вижу зря. Прежде всего - потому что строками читать конечно же нельзя (пока в строке под миллион аллелей а в будущем может быть до 2х и может и больше), во вторых потому что думал что getc всё равно берёт из буфера, а оверхеды вызовов из функций инлайнятся. Получается, что циклический буфер малого размера должен сильно помочь.

Но fread( p, 1, 200000000, f ), конечно, напрягает (на некоторых моих машинах и памяти-то такой нет). Хотелось бы не отжирать память.

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

* ()
[#] Ответ на: комментарий от aho 30.04.2011 17:46:15  


>> Нет :D Ты отмонтировал ФС между тестами? %)


> там файл в 60Мб - читается около секунды на "свежей" ФС


Ну, мне-то нетрудно проверить честно, с отмонтированием. "a" - это твой вариант 1, "b" - "более правильный вариант"

time ./a /media/PENDRIVE/debian.img

real 1m10.208s
user 0m47.807s
sys 0m1.564s

time ./b /media/PENDRIVE/debian.img

real 1m10.504s
user 0m6.640s
sys 0m1.456s

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

Ах да, размер debian.img - 2Г.

***** ()
[#] Ответ на: комментарий от siberean 30.04.2011 17:56:04  

> Попробую наверное взять что-то типа мегабайта.

надеюсь, ты помнишь о setvbuf? ;)

***** ()
[#] Ответ на: комментарий от memnek 30.04.2011 10:52:07  
siberean

> он страшный и не мимикрирует под нужный тулкит

Мне по барабану тулкит. TCL/Tk ещё страшнее. И даже тот работает и очень даже портабельно.
Я когда-то делал проги для счёта (регрессии, планирование), которую рассылали всем госпиталям, или прогу, которая делает какой-нить процессинг.
В одном случае на TCL/Tk (под ней - сишная прога), в другом - на джаве. Ни в том ни в другом случае - ни у одного из клиентов проблем с гуёй не возникало.
Все файлы выбирались через гую очень хорошо, клиенты были довольны. А в случае джавы - зато ещё всякие прогресс-бары (работающие в параллельных тредах с процессингом) и файло-селекторы, менюшки были, совсем хорошо. Свинг несравнимо богаче тикля.
Не буду говорить, сколько накушаетесь, если будете шиппить что-то дотнеттовское, со многими версиями дот нетов и многими версиями виндовс (а если у юзера вин2к или вообще вин98?).

Я же для пользователей оффтопика делал один win32 дистрибутив, положив jre внутрь, всё зипповал, и юзер просто раззиповывал и шлёпал на .bat файл.

Как здесь:
http://sourceforge.net/projects/aisconvert/files/aisconvert-1.11-bin-with-jre...
(на работе я использовал тот же подход, что и в aisconvert, но код конечно же был совершенно другой).

На чём предлагаете Вы?

* ()
[#] Ответ на: комментарий от tailgunner 30.04.2011 18:09:14  
siberean

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

* ()
[#] Ответ на: комментарий от tailgunner 30.04.2011 18:07:53  
aho

> Ну, мне-то нетрудно проверить честно, с отмонтированием

проверил у себя - действительно разница действительно гораздо меньше на больших некэшированных файлах, без буфера - 38сек, с буфером - 33сек, 2.2Гб файл

()
[#] Ответ на: комментарий от DNA_Seq 30.04.2011 11:43:39  
sergej

>> На си вообще обработка строк через жопу сделана. А что на числодробилках жаба идет вровень с си не новость.

>> местами жаба в полтора раза быстрее си

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

***** ()
[#] Ответ на: комментарий от sergej 30.04.2011 18:22:40  
DNA_Seq

>Но ведь это говорит лишь о том, что код на си написан не очень хорошо

это может говорить о неоптипамльности:
1) кода
2) библиотек
3) компилятора

Либы и компилятор тоже переписывать будешь?

*** ()