Re: Интересное мнение про перспективы .NET

User avatar
Интеррапт
Уже с Приветом
Posts: 17281
Joined: 07 Sep 2011 10:05
Location: Seattle, WA

Re: Re: Интересное мнение про перспективы .NET

Post by Интеррапт »

crypto5 wrote:
Интеррапт wrote:
crypto5 wrote:
Интеррапт wrote:вызова complete(), но все это время там просто поток будет тупо сидеть в join().
Здесь я думаю у тебя ошибка в утверждении, когда вызовется join(), ForkJoinPool увидит что ReadFromNetTask еще не релизнута, и не будет блокировать поток, а отдаст потоку какую то другую таску из очереди выполнения.
Я не вижу в этом ни малейшего performance gain по сравнению с, например AsynchronousSocketChannel (и его производных), который просто возвращает Future на read.
Разница в том что "Future на read" будет блокировать лишний поток пока рид не завершился, а в ForkJoin pool-e поток блокироваться не будет, а будет выполнять другую полезную работу.
И чем же это будет отличаться (в лучшую сторону) от моего асинхронного чтения через NIO, например

цикл
{
connect
asynchronous_socket.read(buffer, new CompletionHandler() {
... записали результат куда-то в базу данных ...
}
}
User avatar
crypto5
Уже с Приветом
Posts: 4637
Joined: 24 Oct 2009 01:38
Location: Chicago ;-) -> SFBA!

Re: Re: Интересное мнение про перспективы .NET

Post by crypto5 »

Интеррапт wrote:
crypto5 wrote:
Интеррапт wrote:
crypto5 wrote:
Интеррапт wrote:вызова complete(), но все это время там просто поток будет тупо сидеть в join().
Здесь я думаю у тебя ошибка в утверждении, когда вызовется join(), ForkJoinPool увидит что ReadFromNetTask еще не релизнута, и не будет блокировать поток, а отдаст потоку какую то другую таску из очереди выполнения.
Я не вижу в этом ни малейшего performance gain по сравнению с, например AsynchronousSocketChannel (и его производных), который просто возвращает Future на read.
Разница в том что "Future на read" будет блокировать лишний поток пока рид не завершился, а в ForkJoin pool-e поток блокироваться не будет, а будет выполнять другую полезную работу.
И чем же это будет отличаться (в лучшую сторону) от моего асинхронного чтения через NIO, например

цикл
{
connect
asynchronous_socket.read(buffer, new CompletionHandler() {
... записали результат куда-то в базу данных ...
}
}
Тем что если у тебя в completionhandler нужно еще сделать какую то асинхронную операцию, а потом еще и еще, и компоновать это все в результаты для вызывающего клиентa, то это все разветвится в callback hell.
Ну и я не сильно шарю как НИО внутри устроен, но там внутри должен быть уже какой то тред пул что бы выполнять в нем колбеки, т.е. по сути тоже самое получится, только код некрасивый будет.
In vino Veritas!
User avatar
Интеррапт
Уже с Приветом
Posts: 17281
Joined: 07 Sep 2011 10:05
Location: Seattle, WA

Re: Re: Интересное мнение про перспективы .NET

Post by Интеррапт »

crypto5 wrote: Тем что если у тебя в completionhandler нужно еще сделать какую то асинхронную операцию, а потом еще и еще, и компоновать это все в результаты для вызывающего клиентa, то это все разветвится в callback hell.
Ну и я не сильно шарю как НИО внутри устроен, но там внутри должен быть уже какой то тред пул что бы выполнять в нем колбеки, т.е. по сути тоже самое получится, только код некрасивый будет.
Да, там пул. Код будет очень даже красивый и вполне предсказуемый, в отличие от ForkJoin Pool.
Кстати, почитай интересную статейку, где разбираются недостатки FJ Pool:

http://coopsoft.com/ar/CalamityArticle.html
User avatar
crypto5
Уже с Приветом
Posts: 4637
Joined: 24 Oct 2009 01:38
Location: Chicago ;-) -> SFBA!

Re: Re: Интересное мнение про перспективы .NET

Post by crypto5 »

Интеррапт wrote:
crypto5 wrote: Тем что если у тебя в completionhandler нужно еще сделать какую то асинхронную операцию, а потом еще и еще, и компоновать это все в результаты для вызывающего клиентa, то это все разветвится в callback hell.
Ну и я не сильно шарю как НИО внутри устроен, но там внутри должен быть уже какой то тред пул что бы выполнять в нем колбеки, т.е. по сути тоже самое получится, только код некрасивый будет.
Да, там пул. Код будет очень даже красивый и вполне предсказуемый, в отличие от ForkJoin Pool.
Ну напиши мне решение задачи про сложение чисел из двух асинхронных колов на колбеках, и посмотрим будет ли он красивым ))
Заодно расскажи что некрасивого и непредсказуемого в моем коде?
Кстати, почитай интересную статейку, где разбираются недостатки FJ Pool:

http://coopsoft.com/ar/CalamityArticle.html
Я не люблю читать длинные статьи от noname senior developers, если человек не может сжато изложить свои мысли в вступлении или выводах, он недостоен моего внимания ))
Но если ты в кратце напишешь несколькими предложениями о чем там, я буду благодарен. :hlop:
In vino Veritas!
User avatar
Интеррапт
Уже с Приветом
Posts: 17281
Joined: 07 Sep 2011 10:05
Location: Seattle, WA

Re: Re: Интересное мнение про перспективы .NET

Post by Интеррапт »

crypto5 wrote:Ну напиши мне решение задачи про сложение чисел из двух асинхронных колов на колбеках, и посмотрим будет ли он красивым ))
Для такой задачи воспользуюсь обычными Future

http://docs.oracle.com/javase/7/docs/ap ... yteBuffer)

Этот метод является просто оберткой вокруг read с CompletionHandler.
User avatar
crypto5
Уже с Приветом
Posts: 4637
Joined: 24 Oct 2009 01:38
Location: Chicago ;-) -> SFBA!

Re: Re: Интересное мнение про перспективы .NET

Post by crypto5 »

Интеррапт wrote:
crypto5 wrote:Ну напиши мне решение задачи про сложение чисел из двух асинхронных колов на колбеках, и посмотрим будет ли он красивым ))
Для такой задачи воспользуюсь обычными Future

http://docs.oracle.com/javase/7/docs/ap ... yteBuffer)

Этот метод является просто оберткой вокруг read с CompletionHandler.
Прекрасно, только твой Future заблокирует вызывающий поток. Пошли по третьему кругу..
In vino Veritas!
User avatar
Интеррапт
Уже с Приветом
Posts: 17281
Joined: 07 Sep 2011 10:05
Location: Seattle, WA

Re: Re: Интересное мнение про перспективы .NET

Post by Интеррапт »

crypto5 wrote:
Интеррапт wrote:
crypto5 wrote:Ну напиши мне решение задачи про сложение чисел из двух асинхронных колов на колбеках, и посмотрим будет ли он красивым ))
Для такой задачи воспользуюсь обычными Future

http://docs.oracle.com/javase/7/docs/ap ... yteBuffer)

Этот метод является просто оберткой вокруг read с CompletionHandler.
Прекрасно, только твой Future заблокирует вызывающий поток. Пошли по третьему кругу..
В твоем случае тоже заблокирует вызывающий поток, а как ты думаешь твой .get() ждать будет? Только кроме этого у тебя еще твой код может на нескольких потоках сидеть, потому что ты асинхронное чтение завернул в потоки (пусть даже те, которые переодически переключаются на другие задачи) и все-таки в большинстве случаях у тебя несколько потоков будут сидеть в idle. А в моем случае - только один поток используется (и кратковременно могут потоки браться из thread pool для completionhandler).
User avatar
crypto5
Уже с Приветом
Posts: 4637
Joined: 24 Oct 2009 01:38
Location: Chicago ;-) -> SFBA!

Re: Re: Интересное мнение про перспективы .NET

Post by crypto5 »

Интеррапт wrote:
crypto5 wrote:
Интеррапт wrote:
crypto5 wrote:Ну напиши мне решение задачи про сложение чисел из двух асинхронных колов на колбеках, и посмотрим будет ли он красивым ))
Для такой задачи воспользуюсь обычными Future

http://docs.oracle.com/javase/7/docs/ap ... yteBuffer)

Этот метод является просто оберткой вокруг read с CompletionHandler.
Прекрасно, только твой Future заблокирует вызывающий поток. Пошли по третьему кругу..
В твоем случае тоже заблокирует вызывающий поток, а как ты думаешь твой .get() ждать будет?
Я уже это описывал несколько раз, get() ждать не будет, он увидит что Future еще не релизнута, и отдаст таску обратно в очередь, а поток займется другой таской, потoки блокироваться не будут.
In vino Veritas!
User avatar
Интеррапт
Уже с Приветом
Posts: 17281
Joined: 07 Sep 2011 10:05
Location: Seattle, WA

Re: Re: Интересное мнение про перспективы .NET

Post by Интеррапт »

crypto5 wrote: Я уже это описывал несколько раз, get() ждать не будет, он увидит что Future еще не релизнута, и отдаст таску обратно в очередь, а поток займется другой таской, потoки блокироваться не будут.
Вот только выдумывать не нужно. Если ты get вызвал из какого-то своего потока, то этот поток будет просто тупо заблокирован при помощи обычного wait. У них даже функция есть специальная (смотрим на исходники):

http://grepcode.com/file_/repo1.maven.o ... /?v=source

Code: Select all

   /**
     * Blocks a non-worker-thread until completion or interruption or timeout.
     */
    private int externalInterruptibleAwaitDone(long millis)
        throws InterruptedException {
        int s;
        if (Thread.interrupted())
            throw new InterruptedException();
        if ((s = status) >= 0) {
            synchronized (this) {
                while ((s = status) >= 0) {
                    if (s == 0)
                        UNSAFE.compareAndSwapInt(this, statusOffset,
                                                 0, SIGNAL);
                    else {
                        wait(millis);
                        if (millis > 0L)
                            break;
                    }
                }
            }
        }
        return s;
    }
которая вызывается отсюда:

Code: Select all

public final V get() throws InterruptedException, ExecutionException {
        int s = (Thread.currentThread() instanceof ForkJoinWorkerThread) ?
            doJoin() : externalInterruptibleAwaitDone(0L);
        Throwable ex;
        if (s == CANCELLED)
            throw new CancellationException();
        if (s == EXCEPTIONAL && (ex = getThrowableException()) != null)
            throw new ExecutionException(ex);
        return getRawResult();
    }
как видишь, твой get() будет вызван из твоего основного потока и поэтому будет вызван externalInterruptibleAwaitDone(0)

т.е. в результате будет просто вызван Object.wait(0) на вызывающем потоке и этот поток заблокируется, без каких-либо волшебных ожиданий.
User avatar
crypto5
Уже с Приветом
Posts: 4637
Joined: 24 Oct 2009 01:38
Location: Chicago ;-) -> SFBA!

Re: Re: Интересное мнение про перспективы .NET

Post by crypto5 »

Да, потому что нужно не get() смотреть, а join().
In vino Veritas!
User avatar
Интеррапт
Уже с Приветом
Posts: 17281
Joined: 07 Sep 2011 10:05
Location: Seattle, WA

Re: Re: Интересное мнение про перспективы .NET

Post by Интеррапт »

crypto5 wrote:Да, потому что нужно не get() смотреть, а join().
Смотрим join (вернее его реальную имплементацию):

Code: Select all

   public final V join() {
        if (doJoin() != NORMAL)
            return reportResult();
        else
            return getRawResult();
    }

   /**
     * Primary mechanics for join, get, quietlyJoin.
     * @return status upon completion
     */
    private int doJoin() {
        Thread t; ForkJoinWorkerThread w; int s; boolean completed;
        if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
            if ((s = status) < 0)
                return s;
            if ((w = (ForkJoinWorkerThread)t).unpushTask(this)) {
                try {
                    completed = exec();
                } catch (Throwable rex) {
                    return setExceptionalCompletion(rex);
                }
                if (completed)
                    return setCompletion(NORMAL);
            }
            return w.joinTask(this);
        }
        else
            return externalAwaitDone();
    }
которая вызывает externalAwaitDone, код которой я уже выше привел и там wait.
User avatar
crypto5
Уже с Приветом
Posts: 4637
Joined: 24 Oct 2009 01:38
Location: Chicago ;-) -> SFBA!

Re: Re: Интересное мнение про перспективы .NET

Post by crypto5 »

Ты просто не в тот бренч логики смотришь, ты смотришь когда Thread не из ForkJoin pool-a, если он из пула вызывается doJoin(), и там все по другому.
In vino Veritas!
User avatar
Интеррапт
Уже с Приветом
Posts: 17281
Joined: 07 Sep 2011 10:05
Location: Seattle, WA

Re: Re: Интересное мнение про перспективы .NET

Post by Интеррапт »

crypto5 wrote:Ты просто не в тот бренч логики смотришь, ты смотришь когда Thread не из ForkJoin pool-a, если он из пула вызывается doJoin(), и там все по другому.
Как это все по другому, если ты get() или join() будешь звать из своего основного потока? Нет там ничего "все по другому".
User avatar
crypto5
Уже с Приветом
Posts: 4637
Joined: 24 Oct 2009 01:38
Location: Chicago ;-) -> SFBA!

Re: Re: Интересное мнение про перспективы .NET

Post by crypto5 »

Интеррапт wrote:
crypto5 wrote:Ты просто не в тот бренч логики смотришь, ты смотришь когда Thread не из ForkJoin pool-a, если он из пула вызывается doJoin(), и там все по другому.
Как это все по другому, если ты get() или join() будешь звать из своего основного потока? Нет там ничего "все по другому".
Нет, в коде который я выписывал оно будет вызываться в тредпуле: ForkJoinTask task = forkJoinPool.submit(() -> f1.get() + f2.get());
In vino Veritas!
User avatar
Интеррапт
Уже с Приветом
Posts: 17281
Joined: 07 Sep 2011 10:05
Location: Seattle, WA

Re: Re: Интересное мнение про перспективы .NET

Post by Интеррапт »

crypto5 wrote:
Интеррапт wrote:
crypto5 wrote:Ты просто не в тот бренч логики смотришь, ты смотришь когда Thread не из ForkJoin pool-a, если он из пула вызывается doJoin(), и там все по другому.
Как это все по другому, если ты get() или join() будешь звать из своего основного потока? Нет там ничего "все по другому".
Нет, в коде который я выписывал оно будет вызываться в тредпуле: ForkJoinTask task = forkJoinPool.submit(() -> f1.get() + f2.get());
Нет.

Если у тебя логика

void myWorkingThread()
{
MyFJTask t1 = ...
MyFJTask t2 = ...
t1.get() + t2.get() // блокируется поток
}

то будет заблокирован wait-ом поток в котором исполняется myWorkingThread(). Это аналогично моему примеру просто с асинхронным read, только у тебя при этом еще наплодятся потоки для t1 и t2.
reality
Уже с Приветом
Posts: 256
Joined: 14 Jul 2011 09:07
Location: SaintP -> NYC

Re: Re: Интересное мнение про перспективы .NET

Post by reality »

Интеррапт wrote: то будет заблокирован wait-ом поток в котором исполняется myWorkingThread(). Это аналогично моему примеру просто с асинхронным read, только у тебя при этом еще наплодятся потоки для t1 и t2.
+1. Нету у форк-джойна возможности неявно блокирующий вызов превратить в неблокирующий.
User avatar
Интеррапт
Уже с Приветом
Posts: 17281
Joined: 07 Sep 2011 10:05
Location: Seattle, WA

Re: Re: Интересное мнение про перспективы .NET

Post by Интеррапт »

reality wrote:
Интеррапт wrote: то будет заблокирован wait-ом поток в котором исполняется myWorkingThread(). Это аналогично моему примеру просто с асинхронным read, только у тебя при этом еще наплодятся потоки для t1 и t2.
+1. Нету у форк-джойна возможности неявно блокирующий вызов превратить в неблокирующий.
Ну да. Не может же он каким-то волшебным образом засунуть caller thread сам к себе в пул :) Собственно оттуда там и вся эта логика, которая проверяет, кто вызывает join и в зависимости от обстоятельств (caller thread в нашем случае) - просто использует обычный блокировочный wait.
User avatar
crypto5
Уже с Приветом
Posts: 4637
Joined: 24 Oct 2009 01:38
Location: Chicago ;-) -> SFBA!

Re: Re: Интересное мнение про перспективы .NET

Post by crypto5 »

Интеррапт wrote:
reality wrote:
Интеррапт wrote: то будет заблокирован wait-ом поток в котором исполняется myWorkingThread(). Это аналогично моему примеру просто с асинхронным read, только у тебя при этом еще наплодятся потоки для t1 и t2.
+1. Нету у форк-джойна возможности неявно блокирующий вызов превратить в неблокирующий.
Ну да. Не может же он каким-то волшебным образом засунуть caller thread сам к себе в пул :) Собственно оттуда там и вся эта логика, которая проверяет, кто вызывает join и в зависимости от обстоятельств (caller thread в нашем случае) - просто использует обычный блокировочный wait.
В моем коде caller thread это thread из пула.
In vino Veritas!
User avatar
Интеррапт
Уже с Приветом
Posts: 17281
Joined: 07 Sep 2011 10:05
Location: Seattle, WA

Re: Re: Интересное мнение про перспективы .NET

Post by Интеррапт »

crypto5 wrote:В моем коде caller thread это thread из пула.
Угу. И тот кто вызывает этот код - тоже из пула. И тот кто вызывает вызывателя - тоже из пула. А как ты данные собираешься обратно самому главному вызывальщику отдавать, который (я надеюсь) не из пула? Ведь ему же нужно получить результаты твоих вычислений. А это значит, что этот злодей где-то ждет с get() (или join), который крутится на потоке не из FJ пула.
User avatar
crypto5
Уже с Приветом
Posts: 4637
Joined: 24 Oct 2009 01:38
Location: Chicago ;-) -> SFBA!

Re: Re: Интересное мнение про перспективы .NET

Post by crypto5 »

Интеррапт wrote:
crypto5 wrote:В моем коде caller thread это thread из пула.
Угу. И тот кто вызывает этот код - тоже из пула. И тот кто вызывает вызывателя - тоже из пула. А как ты данные собираешься обратно самому главному вызывальщику отдавать, который (я надеюсь) не из пула? Ведь ему же нужно получить результаты твоих вычислений. А это значит, что этот злодей где-то ждет с get() (или join), который крутится на потоке не из FJ пула.
Самый последний вызывающий конечно будет блокироваться. Вся эта система нужна если промежуточных операций десятки и сотни, вот промежуточные операции блокировать треды не будут.
In vino Veritas!
reality
Уже с Приветом
Posts: 256
Joined: 14 Jul 2011 09:07
Location: SaintP -> NYC

Re: Re: Интересное мнение про перспективы .NET

Post by reality »

Ну окей, вроде действительно такая схема будет работать и действительно она будет неблокирующей. Только количество кода и абстакций тут зашкаливает, а как это дебажить так я вообще боюсь себе представить. Если вдруг какой то результат вычислений не уходит в неблокирующий сокет то как развзять этот клубок из тасков я не представляю.
User avatar
crypto5
Уже с Приветом
Posts: 4637
Joined: 24 Oct 2009 01:38
Location: Chicago ;-) -> SFBA!

Re: Re: Интересное мнение про перспективы .NET

Post by crypto5 »

reality wrote:Ну окей, вроде действительно такая схема будет работать и действительно она будет неблокирующей. Только количество кода и абстакций тут зашкаливает, а как это дебажить так я вообще боюсь себе представить. Если вдруг какой то результат вычислений не уходит в неблокирующий сокет то как развзять этот клубок из тасков я не представляю.
Какое такое количество кода и абстракций в моем примере: ForkJoinTask task = forkJoinPool.submit(() -> f1.get() + f2.get()); ?
Ну и с сложностью дебага я согласен, но такая же фигня будет в случае скалы.
In vino Veritas!
User avatar
Интеррапт
Уже с Приветом
Posts: 17281
Joined: 07 Sep 2011 10:05
Location: Seattle, WA

Re: Re: Интересное мнение про перспективы .NET

Post by Интеррапт »

crypto5 wrote:
Интеррапт wrote:
crypto5 wrote:В моем коде caller thread это thread из пула.
Угу. И тот кто вызывает этот код - тоже из пула. И тот кто вызывает вызывателя - тоже из пула. А как ты данные собираешься обратно самому главному вызывальщику отдавать, который (я надеюсь) не из пула? Ведь ему же нужно получить результаты твоих вычислений. А это значит, что этот злодей где-то ждет с get() (или join), который крутится на потоке не из FJ пула.
Самый последний вызывающий конечно будет блокироваться. Вся эта система нужна если промежуточных операций десятки и сотни, вот промежуточные операции блокировать треды не будут.
Десятки и сотни? А прости, как ты собираешься эти сотни асинхронных чтений засовывать в свой FJ пул, если асинхронный read вызывает CompetionHandler через свой собственный пул потоков? Т.е. мало того, что ты будешь использовать потоковый пул NIO, так ты еще поверх этого собрался FJ потоки накручивать.
reality
Уже с Приветом
Posts: 256
Joined: 14 Jul 2011 09:07
Location: SaintP -> NYC

Re: Re: Интересное мнение про перспективы .NET

Post by reality »

А в случае скалы есть Higher Order Types и можно свой сервис параметризовать монадой Future для продакшена или монадой Id для тестов/дебага и тогда все вызовы будут делаться на стеке в одном потоке что делает дебаг абсолютно элементарным. А в джаве это не сделать никак. Ну точнее наверняка можно что то придумать но опять же это явно будет не 1 строчка.
User avatar
crypto5
Уже с Приветом
Posts: 4637
Joined: 24 Oct 2009 01:38
Location: Chicago ;-) -> SFBA!

Re: Re: Интересное мнение про перспективы .NET

Post by crypto5 »

Интеррапт wrote:
crypto5 wrote:
Интеррапт wrote:
crypto5 wrote:В моем коде caller thread это thread из пула.
Угу. И тот кто вызывает этот код - тоже из пула. И тот кто вызывает вызывателя - тоже из пула. А как ты данные собираешься обратно самому главному вызывальщику отдавать, который (я надеюсь) не из пула? Ведь ему же нужно получить результаты твоих вычислений. А это значит, что этот злодей где-то ждет с get() (или join), который крутится на потоке не из FJ пула.
Самый последний вызывающий конечно будет блокироваться. Вся эта система нужна если промежуточных операций десятки и сотни, вот промежуточные операции блокировать треды не будут.
Десятки и сотни? А прости, как ты собираешься эти сотни асинхронных чтений засовывать в свой FJ пул, если асинхронный read вызывает CompetionHandler через свой собственный пул потоков? Т.е. мало того, что ты будешь использовать потоковый пул NIO, так ты еще поверх этого собрался FJ потоки накручивать.
Сотни операций а не чтений, операций может быть и меньше. Но собственно где именно ты видишь проблему даже если чтений и сотни?
In vino Veritas!

Return to “Работа и Карьера в IT”