ФлеймФорумПроЭкты

Ü (Programmiersprache) (99 стр)

Страницы: 195 96 97 98 99 100 Следующая »
#1470
0:56, 25 мар 2023

Имбирная Ведьмочка
> и кроме крестов он так больше нигде и не работает
Почему это?

function foo(condition0, condition1){
    switch(condition0){
        case true:
            let some_non_trivial = 42
            if(condition1){
                console.log("finally do something " + some_non_trivial)
                break
            }
        default:
            console.log("do somethig else")
    }
}
#1471
6:03, 25 мар 2023

Имбирная Ведьмочка
> Кстати, самый очевидный вариант, как обычно, приходит на следующий день:
Мне кажется, что тут идеальна была бы конструкция типа однократного цикла:

block
{
    if( condition0 )
    {
        // some non-trivial code with variables declaration here
        // ...
        if( condition1 )
        {
            // finally do something
            break;  // to the end of the block
        }
    }
    // do somethig else
}

Здесь block {...} — это эквивалент do {...} while(false);
Еще разрешить многократный break для выхода из нескольких вложенных циклов/блоков и можно будет достаточно сложные графы исполнения реализовывать.

#1472
7:00, 25 мар 2023

}:+()___ [Smile]
> Еще разрешить многократный break для выхода из нескольких вложенных
> циклов/блоков и можно будет достаточно сложные графы исполнения реализовывать.
В очередной раз скажу, что я строго против многократных брейков в пользу нормального гоуту, ну или хотя бы "у нас есть гоуту дома" когда гоуту дома это брейк с пометкой.

Это всё равно, что нормальные именованные локальные переменные променять на смещения по стеку, причём стек постоянно перемещается между скоупами, так что одна и та же переменная в разных местах оказывается stack 1, stack 2, stack 4 и stack 7.

}:+()___ [Smile]
> Мне кажется, что тут идеальна была бы конструкция типа однократного цикла:
А если этой конструкции дать свой лейбл — останется буквально миллиметр до того, чтобы вырефакторить её в отдельную функцию с обычным ретурном.

#1473
7:39, 25 мар 2023

Имбирная Ведьмочка
> В очередной раз скажу, что я строго против многократных брейков в пользу нормального гоуту
У кого по пять вложенных циклов, тот ССЗБ, а у меня обычно два вложения, на крайняк, три, городить ради этого всякие метки — это лишнее усложнение. Тогда уж сразу любой break/continue с меткой надо, чего мелочиться.

> А если этой конструкции дать свой лейбл — останется буквально миллиметр до того, чтобы вырефакторить её в отдельную функцию с обычным ретурном.
Если там огромный контекст протягивается снаружи внутрь, то далеко не миллиметр.

#1474
8:42, 25 мар 2023

Хех. В Rust ведь блоки это выражения, так что можно так:

fn bar() -> i32
{
    42
}

fn foo(condition : bool)
{
    if condition && { let v = bar(); v > 24 }
    {
        println!("alternative0");
    }
    else
    {
        println!("alternative1");
    }
}

pub fn main() {
    foo(true);
    foo(false);
}

Хотя это не совсем эквивалентно тому, что я хочу. Переменные из блока внутри условия не доступны внутри блока if.

#1475
9:57, 25 мар 2023

}:+()___ [Smile]
> Если там огромный контекст протягивается снаружи внутрь, то далеко не
> миллиметр.
Заверни в объект.

Ну или, как я уже сказал, если функция прямо упирается ногами и не выносится — я считаю, что простой goto after будет самым понятным решением, в силу своей простоты и очевидности:

    if( condition0 )
    {
        // some non-trivial code with variables declaration here
        // ...
        if( condition1 )
        {
            // finally do something
            goto something_done;
        }
    }
    // do somethig else
something_done:

...Что-что говорите, язык не поддерживает гоуту? Ну значит, язык — говно, и остаётся только страдать. 🗿

#1476
13:18, 25 мар 2023

Начал эксперименты с llvm-ными корутинами.
Вот такой код:

fn generator Foo( u32 count ) : u32
{
  for( var u32 mut x= 0u; x < count; ++x )
  {
    yield x;
  }
}

fn Main( u32 count )
{
  auto gen= Foo(count);
  while( true )
  {
    if_coro_advance( x : gen )
    {
      PrintInt(x);
      continue;
    }
    break;
  }
}

fn PrintInt( u32 x );

С оптимизациями компилируется в такое:

define void @_Z4Mainj(i32 %_arg_count) unnamed_addr #2 comdat {
coro_not_done.lr.ph:
  %.not.i = icmp eq i32 %_arg_count, 0
  br i1 %.not.i, label %._crit_edge, label %coro_not_donethread-pre-split

coro_not_donethread-pre-split:                    ; preds = %coro_not_done.lr.ph, %coro_not_donethread-pre-split
  %0 = phi i32 [ %1, %coro_not_donethread-pre-split ], [ 0, %coro_not_done.lr.ph ]
  tail call void @_Z8PrintIntj(i32 %0)
  %1 = add nuw i32 %0, 1
  %exitcond.not.i = icmp eq i32 %1, %_arg_count
  br i1 %exitcond.not.i, label %._crit_edge, label %coro_not_donethread-pre-split

._crit_edge:                                      ; preds = %coro_not_donethread-pre-split, %coro_not_done.lr.ph
  ret void
}

Изначально то фронтенд компилятора вставляет malloc/free для кадра стека генератора. Но оптимизатор всё заинлайнил и успешно убрал аллокации из кучи.

#1477
13:28, 25 мар 2023

Но вот если сделать вложенные генераторы:

fn generator CountNumbers( u32 count ) : u32
{
  for( var u32 mut x= 0u; x < count; ++x )
  {
    yield x;
  }
}

fn generator CountSquareNumbers( u32 count ) : u32
{
  auto gen= CountNumbers(count);
  while( true )
  {
    if_coro_advance( x : gen )
    {
      yield x * x;
      continue;
    }
    break;
  }
}

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

#1478
16:27, 25 мар 2023

Залез внутрь проходов llvm по подготовке корутин, обнаружил, что я делаю не совсем верно.
Теперь вроде malloc-и уходят при большой вложенности корутин.

+ вот такая цепочка генераторов
+ выдаёт в итоге такой код

malloc-ов нету и это хорошо. Плохо то, что какие-то мусорные undef значения и ветвления всё-же остаются.

#1479
20:09, 25 мар 2023

Чё-то меня оптимизатор llvm вымораживает своей беспомощностью.

+ вот такой казалось бы прямолинейный код

Должен ведь развернуться по-простому.

+ но нет

Ума не приложу, почему тут оптимизатор оставил уродский switch с phi-узлом на десять вариантов. И против такого даже O3 и второй прогон оптимизации не помогают.

#1480
21:31, 29 мар 2023

Panzerschrek[CN]
> Не понятно, при этом, как реализовать взаимодействие с системными асинхронными API. Есть несколько вариантов.
Можно поизучать, как в Rust-овской библиотеке "tokio" это дело сделано.
Есть блог: https://tokio.rs/blog, там есть кое-что интересное.

#1481
21:43, 29 мар 2023

Panzerschrek[CN]
> Второй вариант - в месте вызова асинхронного API вставлять цикл из опроса системного API на предмет готовности запроса, и если тот ещё не готов, переключать асинхронную функцию.
А вот видимо чтобы в холостую не молоть циклы CPU, в Rust внедрён механизм wake. Насколько я понимаю, с ним возможно через какую-нибудь системную очередь сообщений узнавать, какие операции ввода/вывода продвинулись и тем самым помечать соотвествующие async функции, вызов системных API в которых продвинулся.

#1482
10:49, 30 мар 2023

A Waker is a handle for waking up a task by notifying its executor that it is ready to be run.

This handle encapsulates a RawWaker instance, which defines the executor-specific wakeup behavior.

The typical life of a Waker is that it is constructed by an executor, wrapped in a Context, then passed to Future::poll(). Then, if the future chooses to return Poll::Pending, it must also store the waker somehow and call Waker::wake() when the future should be polled again.

Я так понимаю, это абстракция над condition variable?

#1483
11:33, 30 мар 2023

Имбирная Ведьмочка
> Я так понимаю, это абстракция над condition variable?
Может даже тупо atomic.

Только мне не понятно, зачем в Rust для этого сделали отдельный контекст, который скрыто передаётся в каждую async функцию. Не проще ли было в какою-нибудь переменную в thread local storage его положить?

#1484
10:19, 7 апр 2023

Интересную штуку в Rust обнаружил.

async fn fib(x : u32) -> u32
{
    if x <= 1
    {
        return 1;
    }
    fib( x - 1).await + fib(x - 2).await
}

Не компилируется:

1 | async fn fib(x : u32) -> u32
  |                          ^^^ recursive `async fn`
  |
  = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
  = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion

Всё дело в том, как работает в Rust async. Там прямо на уровне фронтенда компилятора под каждую async функцию создаётся уникальный структурный тип, поля которого - это локальные переменные async функции, которые нужно сохранить между await точками. При этом если одна async функция зовёт другую, то структура этой другой функции будет вложенной в структуру первой функции. Из-за этого вот так вот просто невозможно создать рекурсивную async функцию.

В Ü же я планирую сделать иначе. Память под объект async функции выделяется из кучи, а это значит, что с рекурсией проблем нету. Да, при этом есть переголова с выделением памяти из кучи, но она во многом устраняется проходами оптимизации. Есть специальных проход для корутин, который как раз нацелен на устранение излишних аллокаций из кучи. Судя по моим тестам оптимизатор умеет устранять аллокации во вложенных вызовах корутин, что в итоге в сравнении с Rust даст такую же производительность.

Страницы: 195 96 97 98 99 100 Следующая »
ФлеймФорумПроЭкты

Тема в архиве.