LINUX.ORG.RU

Парни из Ричмонда разработали язык Fan на замену C# и Java

 , , , ,


0

0

Устав переписывать программы с Java на C# и обратно, группа лиц разработала новый практичный язык программирования, который предназначен для написания программ в легкой и непринужденной, веселой (fun) манере.

Программы на языке Fan можно запускать как скрипты в браузере (аналогично JavaScript), так и как обычные скрипты (аналогично bash/perl) или десктопные приложения (.exe, elm)

Программы компилируются в промежуточный код fcode, который затем в рантайме транслируется либо в байткод JVM, либо в IL, в зависимости от того, в какой виртуальной машине запустили программу. Также fcode позволит в будущем написать транслятор в Parrot, Object-C либо LLVM.

>>> Подробности

★★★★★

Проверено: Shaman007 ()

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

>Надо будет отбенчить. Но был тормозной :)
питон/Boo вроде тоже не реактивны были...

>Вот когда 1.7 (его в 1.7 ввели, или нет ещё?) будет на каждой машине - тогда будет другое дело... Хотя, опять же, что у него со скоростью? Не щупал...

обещают в 1.6 бэкпортнуть. со скоростью хорошо)

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

>Передавать объекты по значению - это тот ещё мазохизм.

Никакой не мазохизм. Есть пара неудобств, но и только.

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

>Никакой не мазохизм.

Угу. Только на уже весьма примитивном тесте производительность падает в разы и достигает уровня с тем же boost.

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

> Ну и - что у тебя за архитектура?

amd64, но добавление к O2 дополнительно march=native или удаление его же на результат не влияют в пределах возможности измерений. Скорее дело в том, что я использовал O2, а ты O3.

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

> Ну, можно тупо вписать пару десятко переменных и пару десятков геттеров-сеттеров. ИМХО, будет достаточно близко к типовому объекту :)

Не, дело не в этом. Дело в том, что new - абсолютно точно располагает объект в ОЗУ. А простое "статическое" создание располагает объект в кеше процессора. Поэтому тест нужен более хм.. объёмный, что ли. Так-эдак порядка миллиона объектов наверное... да и то может быть мало будет.

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

для стекового варианта с 02 у меня полные 100 циклов заняли всего 50 секунд. Что по 0.5 секунды на цикл. Это дважды быстрее чем ява.

с O3 тоже 50 секунд.. различие в первом знаке после запятой, но на то, что 0.5 сек на цикл это никак не влияет.

для явы только что тест ста циклов дал 106 сек. То есть си++ дважды быстрее.

$ gcc --version gcc (Debian 4.3.4-2) 4.3.4

$ java -version java version "1.6.0_0" OpenJDK Runtime Environment (IcedTea6 1.6.1) (6b16-1.6.1-1) OpenJDK 64-Bit Server VM (build 14.0-b16, mixed mode)

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

Скорее дело в том, что я использовал O2, а ты O3.

Возможно. Проверю. А пока голову над усложнением теста (но не его написания :D - ведь, на многих из тестируемых языков я кроме этого теста ничего не писал) ломаю.

Сейчас пришёл к чему-то типа такого:

class FibObj2
{
    private int _value;
    private FibObj2 _prev1;
    private FibObj2 _prev2;
    private int[] _foo = new int[20];

    private final void _init(int n)
    {
        _prev1 = new FibObj2(n-1);
        _prev2 = new FibObj2(n-2);
    }

    private final void _free()
    {
        _prev1 = null;
        _prev2 = null;
    }

    FibObj2(int n)
    {
        _value = n;

        for(int i=0; i<20; i++)
            _foo[i] = 0;
    }

    public int value()
    {
        if(_value <= 2)
            return 1;

        _init(_value);
        int res = _prev1.value() + _prev2.value() + _prev1.foo() + _prev2.foo();
        _free();
        return res;
    }

    public int foo()
    {
        int sum = 0;
        for(int i=0; i<20; i++)
            sum += _foo[i];

        return sum;
    }
        
    public static void main(String[] argv)
    {
        for(int i=0; i<10; i++)
        {
            FibObj2 x = new FibObj2(33);
            System.out.println(x.value());
        }
    }
}

Тут и передача объектов между методами, и размер объектов больше, и более интенсивная работа с полями объектов...

Сойдёт в таком духе? :)

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

>Дело в том, что new - абсолютно точно располагает объект в ОЗУ. А простое "статическое" создание располагает объект в кеше процессора.

Э... Java тоже аллоцирует память оперативную, а не кеш процессора :) Так что тут - никак...

>Так-эдак порядка миллиона объектов наверное...


При расчёте 33-го числа Фибоначчи из предыдущего теста происходит создание 7 049 155 объектов.

При расчёте 40-го - 204 668 309.

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

[...]

    private int _value;
    private FibObj2 _prev1;
    private FibObj2 _prev2;
    private int[] _foo = new int[20];

Ужас! Ну, и стиль! Видимо, зловредное влияние питона сказывается или еще чего-то там :)

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

По идее, 7 миллионов объектов по 25*4=200+ байтов каждый - это полтора гига оперативки. У Java по дефолту куча в 32Мб (64Мб при -server). Так что работа сборщика мусора ещё неизбежно тестируется. Можно будет ещё отдельно померить сразу резервирование большой кучи, типа -Xmx512m.

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

>Ужас! Ну, и стиль!

Ужасный стиль - это когда всё в одну строку лепится.

...

По существу теста предложения есть?

Или альтернативный вариант? В изящном стиле :)

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

для сановского компилера и виртуальной машины результат примерно тот же - 1:16 ...

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

Вот и сишные ошибки с указателями полезли... Сказываются годы отсутствия практики. Где я тут туплю? (сегфолт при исполнении)

include <stdio.h>.

#define SIZE 20

class FibObj2
{
  private:
     int _value, _foo[SIZE];
     FibObj2 *_prev1, *_prev2;

    void _init(int n)
    {
        _prev1 = new FibObj2(n-1);
        _prev2 = new FibObj2(n-2);
    }

    void _free()
    {
        delete(_prev1);
        delete(_prev2);
    }

  public:
    FibObj2(int n)
    {
        _value = n;
        for(int i=0; i<SIZE; i++)
            _foo[i] = 0;
    }

    int value()
    {
        _init(_value);
        int res = _prev1->value() + _prev2->value() + _prev1->foo() + _prev2->foo();
        _free();
        return res;
    }

    int foo()
    {
        int sum = 0;
        for(int i=0; i<SIZE; i++)
            sum += _foo[i];
        return sum;
    }
};
    
int main()
{   
    for(int i=0; i<3; i++)
    {
        FibObj2 *x = new FibObj2(33);
        printf("n=%d\n", x->value());
        delete(x);
    }
    return 0;
}

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

Вообще, мне этот тест уже заранее больше нравится. Ближе к жизни :) Интересно, какие будут результаты... Жаль, сейчас AMD-шных машин под рукой нет.

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

>if(_value < 3)

Вот, я тормоз :)

...

Сейчас Си++ с хипом вышел шустрее Java. 1,17сек. против 1,33.

Вариант со стеком, похоже, в этом примере невозможен?

KRoN73 ★★★★★
()

Всё у вас тут здорово и познавательно... Смущает только одно:

fUn = веселье, забава

fAn = ВЕНТИЛЯТОР (веер, опахало)

Вы уж определитесь... =)

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

>Сейчас Си++ с хипом вышел шустрее Java. 1,17сек. против 1,33.

А, пардон, это java без -server. С оным ключиком: 0,67 сек.

...

C#/mono - 2,81сек.

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

Java

$ javac FibObj2.java

$ time java FibObj2

real 0m5.017s

user 0m4.848s

sys 0m0.156s

C++

Переправил цикл до 10.

$ g++ -O2 FibObj2.cpp -o FibObj2

$ time ./FibObj2

real 0m8.689s

user 0m8.685s

sys 0m0.004s

Выводы

Java-программа оказалась быстрее, чем аналогичная на C++. Я озадачен...

Окружение

Ноутбук с C2Duo T7700 на борту.

Java HotSpot(TM) Server VM (build 14.0-b16, mixed mode)

g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3

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

> Java-программа оказалась быстрее, чем аналогичная на C++. Я озадачен...

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

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

Дайте ссылку на актуальные исходники - тоже потесчу...

FibObj2 выше по тексту на Java и C++. На C# будет:

using System;

class FibObj2
{
	private int _value;
	private FibObj2 _prev1;
	private FibObj2 _prev2;
	private int[] _foo = new int[20];

	private void _init(int n)
	{
		_prev1 = new FibObj2(n-1);
		_prev2 = new FibObj2(n-2);
	}

	private void _free()
	{
		_prev1 = null;
		_prev2 = null;
	}

	FibObj2(int n)
	{
		_value = n;

		for(int i=0; i<20; i++)
			_foo[i] = 0;
	}

	public int value()
	{
		if(_value <= 2)
			return 1;

		_init(_value);
		int res = _prev1.value() + _prev2.value() + _prev1.foo() + _prev2.foo();
		_free();
		return res;
	}

	public int foo()
	{
		int sum = 0;
		for(int i=0; i<20; i++)
			sum += _foo[i];

		return sum;
	}

	public static void Main()
	{
		for(int i=0; i<10; i++)
		{
			FibObj2 x = new FibObj2(33);
			Console.WriteLine(x.value());
		}
	}
}

Под другие языки ещё не адаптировал. Старый вариант можно посмотреть для массы языков на http://balancer.ru/tech/forum/2008/08/t63003--proizvoditelnost-yazykov-obektn...

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

У меня C++ версия быстрее, чем java без -server:

C++

real	0m4.864s
user	0m4.652s
sys	0m0.004s

Java не -server

real	0m8.561s
user	0m7.959s
sys	0m0.236s

Опция -server не особо помогает:

Java -server

real	0m8.447s
user	0m7.686s
sys	0m0.270s

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

Тогда странно. Ещё по топику http://balancer.ru/tech/forum/2005/03/t32510--ya-ne-veryu-svoim-glazam-java-b... заметили, что на amd64 и/или под Windows Java обычно несколько уступает Си++, а на интеловских системах, случается, обходит.

...

А так - вон, выше ( http://www.linux.org.ru/jump-message.jsp?msgid=4060198&cid=4065018 ) на C2D T7700 Java тоже быстрее оказалась...

Кстати, а ты учёл, что в сишной программе приведённой 3 цикла против 10 у Java?

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

Вот это для интеловской архтектуры вполне ожидаемо.

Есть у кого-нить ещё AMD проверить для подтверждения наблюдаения? :) Там Java должна быть медленнее, чем Си++.

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

последний тест у меня получается ява быстрее чем си++

java -server: 7.5 сек 10 циклов = 0,75 сек на цикл.

g++ -O2: 9.5 сек 10 циклов = 0,95 сек на цикл.

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

А, вы про shared data? Не думал что эту хрень можно назвать сборщиком мусора.

chemikadze
()

Устав от Java и C#, группа лиц разработала^W вбросила вентилятор...

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

>При расчёте 33-го числа Фибоначчи из предыдущего теста происходит создание 7 049 155 объектов.

> При расчёте 40-го - 204 668 309.

Ты ДИБИЛ. Теперь подробнее. при расчёте 33-го числа фиббоначчи ты получаешь ОДНОВРЕМЕННО только около 70 объектов, ибо если рассмотреть число фибоначчи как ветку, то получится нечто такое: http://xmages.net/show.php/496682_.JPG.html

Теперь объясняю смысл картинки (и сорри за хостинг, первое что нашёл). Вычисляем число Фибонначчи для 33. Для этого надо для 32 и 31. для них соответственно 31 и 30, и 30 и 29. Но заметьте, некоторые квадраты отмечены красным. Это те, чьи классы будут существовать одновременно. А всё потому, что ты создаешь классы только когда надо посчитать числа, и сразу же удаляешь. а ты создавай классы при создании объекта (в конструкторе) и удаляй при удалении (в деструкторе). тогда прога сразу же будет пожирать тонны памяти, ибо ВСЕ объекты будут сразу в памяти. вот код для С++, для жабы сделай сам, я не жабокодер:

include <stdio.h>.

#define SIZE 20

class FibObj2 {

private:

int _value, _foo[SIZE];

FibObj2 *_prev1, *_prev2;

void _init(int n)

{

_prev1 = new FibObj2(n-1);

_prev2 = new FibObj2(n-2);

}

void _free()

{

delete(_prev1);

delete(_prev2);

}

public:

FibObj2(int n) // конструктор

{ _value = n;

for(int i=0; i<SIZE; i++)

_foo[i] = 0;

_init(_value); // создаю сразу в конструкторе потомков

}

~FibObj2() // деструктор

{

_free(); // и только в деструкторе удаляю их

}

int value()

{ if (_value < 3) return 1;

int res = _prev1->value() + _prev2->value() + _prev1->foo() + _prev2->foo();

return res;

}

int foo()

{

int sum = 0;

for(int i=0; i<SIZE; i++)

sum += _foo[i];

return sum;

}

}; int main()

{

for(int i=0; i<3; i++)

{

FibObj2 *x = new FibObj2(33);

printf("n=%d\n", x->value());

delete(x);

}

return 0;

}

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

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

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

небольшая поправочка к конструктору, забыл if (n < 3), как и ты когда то:

FibObj2(int n) // конструктор

{ _value = n;

for(int i=0; i<SIZE; i++)

_foo[i] = 0;

if (n < 3) // вот это забыл

_init(_value); // создаю сразу в конструкторе потомков

}

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

чёрт, наоборот, if (n > 2):

FibObj2(int n) // конструктор

{ _value = n;

for(int i=0; i<SIZE; i++)

_foo[i] = 0;

if (n > 2) // а теперь и исправил

_init(_value); // создаю сразу в конструкторе потомков

}

// просто в спешке переделывал код

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

Вариант со стеком, похоже, в этом примере невозможен?

Отчего-же?

#include <stdio.h>
#define SIZE 20
namespace orig
{
class FibObj2
{
private:
  int _value, _foo[SIZE];
  FibObj2 *_prev1, *_prev2;

  void _init ( int n )
  {
    _prev1 = new FibObj2 ( n - 1 );
    _prev2 = new FibObj2 ( n - 2 );
  }

  void _free()
  {
    delete ( _prev1 );
    delete ( _prev2 );
  }

public:
  FibObj2 ( int n )
  {
    _value = n;
    for ( int i = 0; i < SIZE; i++ )
      _foo[i] = 0;
  }

  int value()
  {
    if ( _value <= 2 )
      return 1;
    _init ( _value );
    int res = _prev1->value() + _prev2->value() + _prev1->foo() + _prev2->foo();
    _free();
    return res;
  }

  int foo()
  {
    int sum = 0;
    for ( int i = 0; i < SIZE; i++ )
      sum += _foo[i];
    return sum;
  }
};
}

/* +++++++++++++++++++ */

namespace optimized
{
class FibObj2
{
private:
  int _value, _foo[SIZE];
  FibObj2 *_prev1, *_prev2;

  void _init ( int n )
  {
    _prev1 = new FibObj2 ( n - 1 );
    _prev2 = new FibObj2 ( n - 2 );
  }

  void _free()
  {
    delete ( _prev1 );
    delete ( _prev2 );
  }

public:
  FibObj2 ( int n )
      : _value ( n )
  {
    int * end = _foo + SIZE;
    for ( int * i = _foo; i != end; i++ )
      *i = 0;
  }

  int value()
  {
    if ( _value <= 2 )
      return 1;
    _init ( _value );
    int res = _prev1->value() + _prev2->value() + _prev1->foo() + _prev2->foo();
    _free();
    return res;
  }

  int foo() const
  {
    int sum = 0;
    const int * end = _foo + SIZE;
    for ( const int * i = _foo; i != end; i++ )
      sum += *i;
    return sum;
  }
};
}

/* +++++++++++++++++++ */

namespace stack
{
class FibObj2
{
private:
  int _value, _foo[SIZE];
public:
  FibObj2 ( int n )
      : _value ( n )
  {
    int * end = _foo + SIZE;
    for ( int * i = _foo; i != end; i++ )
      *i = 0;
  }

  int value() const
  {
    if ( _value <= 2 )
      return 1;
    FibObj2 _prev1( _value - 1 );
    FibObj2 _prev2( _value - 2 );
    return _prev1.value() + _prev2.value() + _prev1.foo() + _prev2.foo();
  }

  int foo() const
  {
    int sum = 0;
    const int * end = _foo + SIZE;
    for ( const int * i = _foo; i != end; i++ )
      sum += *i;
    return sum;
  }
};
}

int main()
{
  for ( int i = 0; i < 10; i++ )
  {
    //orig::FibObj2 *x = new orig::FibObj2 ( 33 );
    //optimized::FibObj2 *x = new optimized::FibObj2 ( 33 );
    stack::FibObj2 *x = new stack::FibObj2 ( 33 );
    printf ( "n=%d\n", x->value() );
    delete ( x );
  }
  return 0;
}
legolegs ★★★★★
()
Ответ на: комментарий от legolegs

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

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

Однако, с диспечером памяти си++ опять обходит яву.

Для си++ результат 4.8 сек на 10 циклов (0,48 на цикл), для явы 7.5 сек на десять циклов (0,75 на цикл) (33 число, 10 циклов).

То есть примерно в полтора раза (1,56 если точнее).

Результат для сорокового числа и пяти циклов

си++ 1 мин 8 сек = 68 сек java 1 мин 43 сек = 103 сек

Опять примерно в полтора раза (1,51). То есть в принципе результат сохраняется. Сокращение же разрыва можно интерпритировать как погрешность или как то, что ассимптотически ява будет догонять (а возможно и обгонять) си++.

Результат для 42 числа и двух циклов

с++ 1 мин 11 сек = 71 сек ява 1 мин 49 сек = 109 сек

на этот раз 1,53, поэтому я склонен интерпретировать «догоняние» как погрешность.

Однако, несмотря на гораздо большую близость к реальности, тест всё-таки искусственный, потому что в памяти одновременно находится (для 42го числа) не более 81 объекта, что, согласитесь, несколько далеко от реальности.

С++ код для теста


#include <stdio.h>

#define SIZE 20

#include <stdio.h>
#include <sys/types.h>
#include <iostream>

#define POOLSIZE 100

class FibObj2
{
  private:
     int _value, _foo[SIZE];
     FibObj2 *_prev1, *_prev2;

    void _init(int n)
    {
        _prev1 = new FibObj2(n-1);
        _prev2 = new FibObj2(n-2);
    }

    void _free()
    {
        delete(_prev1);
        delete(_prev2);
    }

  public:
    FibObj2(int n)
    {
        _value = n;
        for(int i=0; i<SIZE; i++)
            _foo[i] = 0;
    }

    int value()
    {
	if(_value < 3)
	    return 1;
        _init(_value);
        int res = _prev1->value() + _prev2->value() + _prev1->foo() + _prev2->foo();
        _free();
        return res;
    }

    int foo()
    {
        int sum = 0;
        for(int i=0; i<SIZE; i++)
            sum += _foo[i];
        return sum;
    }
    
    void* operator new (size_t size);
    void operator delete (void* pointerToDelete);
};


class MemoryManager {
  unsigned int totalInMemory;

  struct FreeStore {
     FreeStore *next;
  };
  FreeStore* freeStoreHead;
  public:
    unsigned int totalInMemoryMax; 
    MemoryManager () {
      freeStoreHead = 0;
      expandPoolSize ();
      totalInMemory = 0;
      totalInMemoryMax = 0;
    }
    virtual ~MemoryManager () {
      cleanUp ();
    }

    inline void* allocate(size_t size) {
      if (0 == freeStoreHead)
        expandPoolSize ();

      FreeStore* head = freeStoreHead;
      freeStoreHead = head->next;
      
      totalInMemory++;
      if(totalInMemoryMax < totalInMemory) totalInMemoryMax = totalInMemory;
      return head;
    }
    

    inline void free(void* deleted) {
      FreeStore* head = static_cast <FreeStore*> (deleted);
      head->next = freeStoreHead;
      freeStoreHead = head;
      totalInMemory--;
    }


    void expandPoolSize () {
    std::cout<<"expanded\n";
      size_t size = (sizeof(FibObj2) > sizeof(FreeStore*)) ? sizeof(FibObj2) : sizeof(FreeStore*);
      FreeStore* head = reinterpret_cast <FreeStore*> (new char[size]);
      freeStoreHead = head;

      for (unsigned int i = 0; i < POOLSIZE; i++) {
        head->next = reinterpret_cast <FreeStore*> (new char [size]);
        head = head->next;
      }

      head->next = 0;
    }

    void cleanUp() {
      FreeStore* nextPtr = freeStoreHead;
      for (; nextPtr; nextPtr = freeStoreHead) {
        freeStoreHead = freeStoreHead->next;
        delete [] nextPtr; // запомните, это был числовой массив
      }
    }
};

MemoryManager gMM;

void* FibObj2::operator new (size_t size) {
  return gMM.allocate(size);
}

void FibObj2::operator delete (void* pointerToDelete) {
  gMM.free(pointerToDelete);
}


int main()
{   
    for(int i=0; i<1; i++)
    {
        FibObj2 *x = new FibObj2(42);
        printf("n=%d\n", x->value());
        delete(x);
    }
    
    std::cout<<"total was "<<gMM.totalInMemoryMax<<" objects in memory\n";
    return 0;
}

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

> "-O3 -march=native"

Честно говоря, с O2 выглядит лучше, правда в пределах погрешности.., а вот -march=native вообще не влияет, похоже.

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

>void* FibObj2::operator new (size_t size) {

В таких случаях нужно писать "inline". Компилятор, конечно, может сам догадаться, да только, зараза, иногда не догадывается.

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

>Честно говоря, с O2 выглядит лучше, правда в пределах погрешности.., а вот -march=native вообще не влияет, похоже.

Странно. У меня O3 дало 3% скорости, а -march=native ещё 14%

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

>Странно. У меня O3 дало 3% скорости, а -march=native ещё 14%
видать у тебя -march=native не дефолтное значение.

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

У твоего диспетчера есть огромный недостаток. Он жрет и не отдает память, пока явно не будет вызван cleanUp(). В "реальной жизни" это иногда неприемлемо.

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