Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Java [игнор отключен] [закрыт для гостей] / Помогите оптимизировать код / 25 сообщений из 41, страница 1 из 2
18.02.2013, 13:40:12
    #38155569
WalterSullivan1
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
Всем привет. Задача: считать из файла набор структур и записать их в базу данных. Структура имеет вид:
char* word; // null-terminated строка
unsigned int32 offset; // сетевой порядок байт
unsigned int32 size; // сетевой порядок байт


Написал следующий код, но меня не устраивает скорость. Есть ли возможность как-то это оптимизировать?

Вот, что говорит профайлер:
self - 15,4%
SQLIteStatement.executeInsert - 39,3%
StringBuilder.append - 11,3%
SQLiteProgram,bundLong - 7,3%
BufferedInputStream.read() - 5,1%
...


Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
String word = "";
long offset = 0;
long size = 0;
short[] shortArray = new short[4];
byte[] byteArray = new byte[4];
long currentIndex = 0;
SQLiteStatement statement = db.compileStatement(
		"INSERT INTO indices" +
		"(IDD, WORD, OFFSET, LENGTH) " +
		"VALUES " +
		"(?, ?, ?, ?);"
		);

statement.bindString(1, String.valueOf(dId));


while(true) {
	result = bufferedInputStream.read();
	
	if (result == -1) {
		break;
	} else if (result == 0) {
		currentIndex++;
		
		word = stringBuilder.toString();
		stringBuilder.delete(0, word.length());
		
		bufferedInputStream.read(byteArray);
		
		for (int i = 0; i < 4; i++) {
			shortArray[i] = (short)(byteArray[i] < 0 ? byteArray[i] + 256 : byteArray[i]);
		}
		offset = (shortArray[0] << 24) | (shortArray[1] << 16) | (shortArray[2] << 8) | shortArray[3];
		
		bufferedInputStream.read(byteArray);
		for (int i = 0; i < 4; i++) {
			shortArray[i] = (short)(byteArray[i] < 0 ? byteArray[i] + 256 : byteArray[i]);
		}
		size = (shortArray[0] << 24) | (shortArray[1] << 16) | (shortArray[2] << 8) | shortArray[3];
		
		
		statement.bindString(2, word);
		statement.bindLong(3, offset);
		statement.bindLong(4, size);
		statement.executeInsert();
		
	} else {
		stringBuilder.append((char)result);
	}
}
bufferedInputStream.close();
}
...
Рейтинг: 0 / 0
18.02.2013, 13:47:17
    #38155580
Озверин
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
WalterSullivan1,

сколько записей в базу вставляете в среднем, распарсив 1 файл?
...
Рейтинг: 0 / 0
18.02.2013, 13:51:27
    #38155590
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
http://www.sqlite.org/faq.html#q19
Для начала использовать batch insert, если цикл повторяется много раз.
Во-вторых выкинуть нафиг StringBuilder - зачем байты туда-сюда перекладывать, по отдельности конвертируя в Char. Читайте в byte[] достаточной длины, а потом просто из него уже делайте String, если это действительно так необходимо. А там без String обойтись никак нельзя?
...
Рейтинг: 0 / 0
18.02.2013, 13:56:18
    #38155602
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
Ещё интересный момент в том что 1 символ это далеко не всегда 1 байт. Для некоторых кодировок ваш код вообще не будет работать.
Кодировки, кстати, хорошо бы указывать явно. На разных девайсах текущая кодировка может быть разной и она не обязательно будет совпадать с кодировкой источника ваших данных.
...
Рейтинг: 0 / 0
18.02.2013, 13:59:11
    #38155607
ivanra
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
Особо не заоптимизируешь, так как
SQLIteStatement.executeInsert - 39,3%
Разве что вынести вставку в отдельный поток, или даже пул, если движок SQLite позволит. Вот пример на 20 потоков:
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
		BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
		ExecutorService service = new ThreadPoolExecutor(
				0, 20, 60L, TimeUnit.SECONDS, queue);
		// цикл чтения
		while (true) {
		...
			service.execute(new Runnable() {
				@Override
				public void run() {
					// задание на вставку
					statement.bindString...
				}
			});
		}// конец цикла
...
Рейтинг: 0 / 0
18.02.2013, 14:07:58
    #38155624
WalterSullivan1
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
Озверинсколько записей в базу вставляете в среднем, распарсив 1 файл?
Тестирую на небольшом файле - 50 000, большие файлы - до 500 000.

BlazkowiczЧитайте в byte[] достаточной длины
Строки - null-terminated, неизвестно сколько читать.

BlazkowiczКодировки, кстати, хорошо бы указывать явно
А как это сделать? В исходном файле кодировка - utf-8.

ivanra , спасибо, попробую.
...
Рейтинг: 0 / 0
18.02.2013, 14:12:16
    #38155638
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
WalterSullivan1BlazkowiczЧитайте в byte[] достаточной длины
Строки - null-terminated, неизвестно сколько читать.

Читайте в буфер, вместо использования BufferedInputStream, а буфер уже сканируйте на терминатор. В этом случае вам нужно будет только склеивать две части byte[] при чтении следующего куска. Это будет намного быстре чем "склеивать" каждый символ.
http://docs.oracle.com/javase/6/docs/api/java/io/InputStream.html#read%28byte [],%20int,%20int%29


WalterSullivan1BlazkowiczКодировки, кстати, хорошо бы указывать явно
А как это сделать? В исходном файле кодировка - utf-8.

Вот конструктор, указываете участок буфера, который слово и явно кодировку.
http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#String(byte [],%20int,%20int,%20java.lang.String)
Нужно будет только исхитрится когда слово разорвано на два буфера.
...
Рейтинг: 0 / 0
18.02.2013, 14:12:26
    #38155639
WalterSullivan1
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
И да, все вставки выполняются внутри транзакции.
...
Рейтинг: 0 / 0
18.02.2013, 14:15:23
    #38155644
ivanra
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
WalterSullivan1 ivanra , спасибо, попробую.
Если этот движок поддерживает batch insert, то конечно, прежде всего надо использовать пакетную вставку, 100-1000 записей за раз (подобрать опытным путем), ну а пакеты уже можно и распараллеливать.
...
Рейтинг: 0 / 0
18.02.2013, 14:16:17
    #38155648
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
ivanraОсобо не заоптимизируешь, так как
SQLIteStatement.executeInsert - 39,3%
Разве что вынести вставку в отдельный поток, или даже пул, если движок SQLite позволит. Вот пример на 20 потоков:

Мысль дельная. Хотя это наверное Андроид и достаточно ресурсов будет не всегда.
Но, в целом, тут можно смело разносить на 3 потока.
1 - чтение
2 - разбивка
3 - вставка - здесь нужна большая очередь, т.к. это самый медленный процесс. Для многопоточной вставки нужно иметь соединение на поток. Вряд ли это что-то даст. Тут нужен просто batch insert.
...
Рейтинг: 0 / 0
18.02.2013, 14:25:33
    #38155668
Озверин
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
WalterSullivan1,

batch инсерт , либо формируйте какой-нить csv файл и потом одной операцией заливайте его в базу(так многократно быстрее будет, чем вставлять 500 000 раз 1 запись)
...
Рейтинг: 0 / 0
18.02.2013, 14:29:59
    #38155675
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
ОзверинWalterSullivan1,
batch инсерт , либо формируйте какой-нить csv файл и потом одной операцией заливайте его в базу(так многократно быстрее будет, чем вставлять 500 000 раз 1 запись)
Если файл формировать локально, то его запись тоже уйму времени может отнять.
...
Рейтинг: 0 / 0
18.02.2013, 14:34:59
    #38155689
ivanra
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
Кстати, строки удобнее читать сканером, а не побайтно:
Код: java
1.
2.
3.
4.
5.
6.
7.
        Scanner scanner = new Scanner(bufferedInputStream, "UTF-8");
        scanner.useDelimiter("");
        while (scanner.hasNext()) {
            word = scanner.next();
            bufferedInputStream.read(byteArray);
            // и т.д.
        }
...
Рейтинг: 0 / 0
18.02.2013, 14:36:08
    #38155690
schwa
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
ivanraWalterSullivan1 ivanra , спасибо, попробую.
Если этот движок поддерживает batch insert, то конечно, прежде всего надо использовать пакетную вставку, 100-1000 записей за раз (подобрать опытным путем), ну а пакеты уже можно и распараллеливать.
Только перед этим стоить почитать о том, что как там с поддержкой мультитрединга в sqlite http://www.sqlite.org/threadsafe.html] http://www.sqlite.org/threadsafe.html , а то мало ли что.
...
Рейтинг: 0 / 0
18.02.2013, 14:54:51
    #38155727
Озверин
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
BlazkowiczОзверинWalterSullivan1,
batch инсерт , либо формируйте какой-нить csv файл и потом одной операцией заливайте его в базу(так многократно быстрее будет, чем вставлять 500 000 раз 1 запись)
Если файл формировать локально, то его запись тоже уйму времени может отнять.

<< времени вставки.
При условии, что рам у нас ограничено сильнее обычного (андроид) - это лучше batch инсертов, я так думаю.
...
Рейтинг: 0 / 0
18.02.2013, 15:01:04
    #38155745
chpasha
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
ОзверинПри условии, что рам у нас ограничено сильнее обычного (андроид) - это лучше batch инсертов, я так думаю.на сколько я понимаю, невозможно импортировать csv в андроиде напрямую. единственный способ ускорения это batch. на счет нескольких писателей в БД в потоках я тоже сильно сомневаюсь, но лень гуглить.
...
Рейтинг: 0 / 0
18.02.2013, 15:22:05
    #38155793
ivanra
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
ivanraКстати, строки удобнее читать сканером, а не побайтно:
Поскольку в данном случае числа записаны в бинарном виде, то сканер не поможет. Разве что написать свою реализацию, используя исходники.
...
Рейтинг: 0 / 0
18.02.2013, 15:25:02
    #38155800
Озверин
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
chpashaОзверинПри условии, что рам у нас ограничено сильнее обычного (андроид) - это лучше batch инсертов, я так думаю.на сколько я понимаю, невозможно импортировать csv в андроиде напрямую. единственный способ ускорения это batch. на счет нескольких писателей в БД в потоках я тоже сильно сомневаюсь, но лень гуглить.

я не знаток андроида, но есть подозрение, что это более зависит от базы.
...
Рейтинг: 0 / 0
18.02.2013, 16:23:09
    #38155954
Лагман
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
еще nio channel можно попробовать например
...
Рейтинг: 0 / 0
18.02.2013, 16:25:02
    #38155960
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
Лагманеще nio channel можно попробовать например
Смысл?
...
Рейтинг: 0 / 0
18.02.2013, 16:30:44
    #38155974
chpasha
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
Озвериня не знаток андроида, но есть подозрение, что это более зависит от базы.
она там одна ;) . т.е. теоретически можно использовать другие, но на практике...
...
Рейтинг: 0 / 0
18.02.2013, 17:20:27
    #38156132
Лагман
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
Blazkowicz,

вдруг байты быстрее поползут )
...
Рейтинг: 0 / 0
18.02.2013, 19:50:35
    #38156356
chabapok
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
я бы сразу в стрнгбилдере конструировал групповой инсерт, прамо из входящих данных, и по достижении определенного размера - отправлял его на выполнение.

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

Параллелить по потокам, на мой взгляд смысла нет, все равно винчестер один (один ведь?), и вы получете затык на io, еще может дыже и хуже работать будет (тут как карта ляжет, пргнозировать сложно), т.к. обращение идет одновременно к исходному файлу и к файлам баз, и как головка будет делать seek - одной ОС известно. Самый быстрый способ - последовательный. На мой взгляд, разносить по потокам имеет смысл только если входной файл и база на разных винчестерах, либа вся база помещается в памяти (но судя по задачи это не ваш случай)

в MyISAM получалось 40000 инсертов-сек, но если добавляешь индексы - производительность может падать на несколько порядков.
...
Рейтинг: 0 / 0
18.02.2013, 20:01:48
    #38156368
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
chabapokя бы сразу в стрнгбилдере конструировал групповой инсерт, прамо из входящих данных, и по достижении определенного размера - отправлял его на выполнение.
Что это даст? Каждый новый SQL запрос будет парсится заново если значения заинлайнить.

chabapokПараллелить по потокам, на мой взгляд смысла нет, все равно винчестер один (один ведь?)

А если SSD? Андроид ведь.


chabapok, и вы получете затык на io, еще может дыже и хуже работать будет (тут как карта ляжет, пргнозировать сложно), т.к. обращение идет одновременно к исходному файлу и к файлам баз, и как головка будет делать seek - одной ОС известно. Самый быстрый способ - последовательный. На мой взгляд, разносить по потокам имеет смысл только если входной файл и база на разных винчестерах, либа вся база помещается в памяти (но судя по задачи это не ваш случай)
На самом деле вычитка пройдёт намного быстрее чем инсерты, поэтому не на что это особо не повлияет. Но есть смысл ограничить размер очереди, чтобы всё в памяти не держать, а заблокировать чтение на время, пока набор данных сбросится в базу.

chabapokв MyISAM получалось 40000 инсертов-сек, но если добавляешь индексы - производительность может падать на несколько порядков.
SQLite же.
...
Рейтинг: 0 / 0
19.02.2013, 08:54:13
    #38156769
WalterSullivan1
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Помогите оптимизировать код
chabapokи еще - напишите сколько строк в секунду у вас вставляется и сколько хотелось бы чтоб вставляло.
Файл на 700 Kb содержит ~50 000 записей, выполнение метода на устройстве занимает 57 секунд, хотелось бы секунд за 20 хотя-бы.

Как я понял SQLite на андроид не поддерживает batch insert (поправьте меня, если я не прав).
Пробовал отключать журналирование, тоже не помогло.
Пробовал вместо одной транзакции на 50 000 делать 50 транзакций по 1000, тоже не быстрее.

Сейчас пытаюсь распараллелить как советовал Blazkowicz .
...
Рейтинг: 0 / 0
Форумы / Java [игнор отключен] [закрыт для гостей] / Помогите оптимизировать код / 25 сообщений из 41, страница 1 из 2
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]