LINUX.ORG.RU

Java vs Python: треды и скорость разработки


0

0

Решил тут сравнить производительность Java и Python в "большой" мультитредовости.

Задача: запустить 10000 тредов, каждый из которых тупо подождёт секунду и закроется. Выйти из системы.

Исходный уровень: совершенно не знаю, как это делается в Питоне и ничего сложнее мелких системных скриптов на нём не писал. На Java довольно плотное программирование около 2.5 лет в весьма крупном проекта, но с тредами сам на низком уровне не работал, только обработка уже запущенных процессов.

Питон: через [b]5 минут[/b] (буквально) копания в Гугле и тестов родил такое:

#!/usr/bin/env python # -*- coding: utf-8 -*-

import threading import time

def proc(n): time.sleep(1)

for i in xrange(10000): threading.Thread(target=proc, name="t"+str(i), args=[i]).start()

Время работы скрипта - *42 секунды*. Расход памяти или ресурсов процессора замечен не был.

Дальше начинается веселье. Я принялся за Java. Как сделать просто запуск тредов я так и не нашёл. Сделал запуск через шедулер. После серии экспериментов исходную задачу... так и не решил :) Треды отрабатывают успешно, но основной процесс не завершается, а так и остаётся запущенным. Если проигнорировать на завершение и закрывать потоки вручную, то выходит код примерно такой:

import java.io.IOException; import java.io.PrintStream; import java.util.Date; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture;

import static java.util.concurrent.TimeUnit.*;

public class test implements Runnable { public static void main(String[] args) { final int MAX=10000;

ScheduledFuture<?>[] t = new ScheduledFuture<?>[MAX];

System.out.println("Init"); for(int i=0; i<MAX; i++) { ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); t[i] = scheduler.schedule(new test(i), 0, SECONDS); }

System.out.println("Stop"); for(int i=0; i<MAX; i++) t[i].cancel(true); System.exit(0); }

int n;

public test(int _n) { n = _n; }

public void run() try { Thread.sleep(1000); } catch(Exception e){} } }

То есть - открываем 10000 тредов, не дожидаясь их завершения (всё равно не работает) тут же закрываем, выходим в систему принудительно.

Облом подстерёг после того, как с тестовых 10 тредов взял 1000. "Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread".

Никакие -Xms/Xmx/Xss не помогали. Рост потребления памяти замечен не был.

5000 - сработало. Время выполнения около *7 секунд.*

Итого, неполноценно реализованный вариант на Java примерно втрое быстрее работающего как задумано на Питоне.

Теперь веселье - на тесты и эксперименты с Java ушло около *45 минут* :D

...

Правда, не всё так радужно. Говорят, у Питона есть страшный Global Interpreter Lock, сильно снижающий эффективность тредов, но я пока недопонял, где на него нарываются :)

...

Кто-то предложит рабочее решение на Java? А то я выдохся ;)

★★★★★
Ответ на: комментарий от Pi

#include <assert.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <stdio.h>

void *func(void *arg)
{
        sleep(1);
        return NULL;
}

int main()
{
        int i, r;
        pthread_attr_t attr;
        pthread_t thread;

        r=pthread_attr_init(&attr); assert(0==r);
        r=pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN+4096); assert(0==r);
        for (i=0; i<10000; ++i) {
                if (pthread_create(&thread, &attr, func, (void *)i)) {
                        printf("%s on %d\n", strerror(errno), i);
                        break;
                }
        }
        r=pthread_attr_destroy(&attr); assert(0==r);
        return 0;
}

real    0m0.612s
user    0m0.048s
sys     0m0.528s

:P

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

Вариант с ожиданием завершения всех потоков:
#include <assert.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <stdio.h>

void *func(void *arg)
{
	int *p=(int *)arg;
	size_t s;
	char c=0;

	sleep(1);
	s=write(p[1], &c, 1); assert(1==s);
	return NULL;
}

int main()
{
	const int MAXT=10000;
	int i, r;
	pthread_attr_t attr;
	pthread_t thread;
	int p[2];
	size_t s;
	char buf[256];

	r=pthread_attr_init(&attr); assert(0==r);
	r=pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN+4096); assert(0==r);
	r=pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); assert(0==r);
	r=pipe(p); assert(0==r);
	for (i=0; i<MAXT; ++i) {
		if (pthread_create(&thread, &attr, func, (void *)p)) {
			printf("%s on %d\n", strerror(errno), i);
			break;
		}
	}
	r=pthread_attr_destroy(&attr); assert(0==r);
	for (i=0; i<MAXT; i+=s)
		s=read(p[0], buf, sizeof(buf)); assert(0<=s);
	close(p[0]); close(p[1]);
	return 0;
}

real    0m1.519s
user    0m0.028s
sys     0m0.400s

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

тогда давай мериться длиной строк и их количеством :)

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

pthread_join будет останавливаться на каждой нити, нужна будет память на 10000 pthread_t

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

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

>Вариант с ожиданием завершения всех потоков:

Вот, и даже на такой низкоуровневой задаче, время выполнения оказалось не столь уж микросокпическим в сравнении с тем же Руби :) Но число строк... :D

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