почти пазл

Всё, что не касается темы рогаликов
Ответить
Аватара пользователя
Xecutor
Мастер
Сообщения: 758
Зарегистрирован: 25 мар 2008, 08:32

почти пазл

Сообщение Xecutor » 10 июл 2014, 08:44

В общем и целом я понимаю, что шанс получить "правильный" ответ близок к 0, но иногда попытка
изложить суть проблемы другому человеку наводит меня на решение :)

После перерыва вернулся к моему язычку программирования, и решил добавить вызов функций с именованными параметрами.
Всё бы ничего, но возник определённый конфликт с необязательными параметрами.
Что бы было понятно, нужно объяснить, как у меня устроен вызов функции с необязательными параметрами.
В целом я придерживаюсь идеологии, что редкоиспользуемые фитчи не должны оказывать
влияния на производительность частоиспользуемых фитч.
Итого. Есть, например, функция:

Код: Выделить всё

func f(a,b,c,d=a+b,e=b+c,f=a+c)
  return a+b+c+d+e+f
end
для неё сгенерируется что-то типа:

Код: Выделить всё

  0: d=a+b
  1: e=b+c
  2: f=a+c
  3:return a+b+c+d+e+f
при вызове в первую очередь проверяется условие: кол-во переданных аргументов совпадает с количеством фактических.
Если совпадает, что точкой входа в функцию будет считаться 3.
Если не совпадает и количество недостающих аргументов <=3, то
точкой входа будет 3 минус кол-во недостающих аргументов.
Дёшево и сердито.
Теперь я добавил вызов с именованными аргументами.

Код: Выделить всё

  f(a=>1,b=>2,c=>3)
в этом случае в точке вызова посчитаются эти аргументы, складутся в словарик, и он будет передан последним параметром.
то есть если будут перемешаны обычные аргументы и именованные, то обычные сразу будут на стэке, а последним будет
словарь с именованными.
При обработке вызова я проверяю, что аргументов не хватает, пробегаюсь по недостающим, ищу их в словаре, и докладываю на стэк.
Теперь ситуация:

Код: Выделить всё

  f(1,2,3,e=>10)
и... я не знаю как сделать так, что бы d и f правильно инициализировались, а e при этом не перетёрся...
единственное потенциальное решение, которое пока пришло в голову, это сделать как-то так:

Код: Выделить всё

  0: if d is not inited then d=a+b
  1: if e is not inited then e=b+c
  2: if f is not inited then f=a+c
  3:return a+b+c+d+e+f
а "is not inited" проверять через спец. флаг в значении которое кладётся на стэк
при инициализации вызова через именованный параметр...
но это вносит небольшой оверхэд в код не относящийся к именованным параметрам.

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

Код: Выделить всё

add b,c => a
то бишь, что бы использовать спец. присваивание, прийдётся сделать как-то так:

Код: Выделить всё

tmp=b+c
a ?= tmp
то бишь это потенциально внесёт даже больше оверхэда, чем условный переход,
ибо вычисления будут производится даже если их результат не нужен...

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

Аватара пользователя
karagy
Сообщения: 1271
Зарегистрирован: 10 янв 2007, 14:13

Re: почти пазл

Сообщение karagy » 10 июл 2014, 12:14

Граф зависимостей параметров строится?
Что скажет интерпретатор (компилятор?) на такую функцию?

Код: Выделить всё

func f(a,b=a+b)
  return a+b
end

Аватара пользователя
Cfyz
Сообщения: 776
Зарегистрирован: 30 ноя 2006, 10:03
Откуда: Санкт-Петербург
Контактная информация:

Re: почти пазл

Сообщение Cfyz » 10 июл 2014, 13:05

Xecutor писал(а):и... я не знаю как сделать так, что бы d и f правильно инициализировались, а e при этом не перетёрся...
Вообще мне кажется, что проверка флага (твой вариант #1) оверхед будет вносить, скажем так, пренебрежимый.

Но уж если очень хочется чтоб совсем без лишних действие в рантайме, то, быть может, изменить способ генерации необязательных параметров? Не ставить код их вычисления в код функции (что, как видим, не дает вычислять их кроме как последовательно), а инлайнить его в место вызова. Необязательные параметры скорее всего будут инициализироваться очень простым выражением или вообще константой, дублирование такого кода не должно существенно повлиять на общий объем. Зато при генерации вызова функции всегда известно что и в каком порядке нужно класть на стек.
karagy писал(а):Что скажет интерпретатор (компилятор?) на такую функцию?

Код: Выделить всё

func f(a,b=a+b)
  return a+b
end
По-моему это можно смело записывать в undefined behaviour, потому что инициализация переменной выражением, в которое входит эта же переменная -- это что-то ненормальное.
Пытается раскуклиться

Аватара пользователя
Xecutor
Мастер
Сообщения: 758
Зарегистрирован: 25 мар 2008, 08:32

Re: почти пазл

Сообщение Xecutor » 10 июл 2014, 13:31

karagy писал(а):Граф зависимостей параметров строится?
Что скажет интерпретатор (компилятор?) на такую функцию?

Код: Выделить всё

func f(a,b=a+b)
  return a+b
end
Компилятор ругнётся, что он не знает, что такое b.
Ибо сначала вычисляется правая часть, а потом регистрируется имя переменной слева.
Но уж если очень хочется чтоб совсем без лишних действие в рантайме, то, быть может, изменить способ генерации необязательных параметров?
Это динамически типизируемый язык.
Когда кодогенератор знает какую именно функцию вызывает в этом месте,
он может оптимизировать вызов. Но если делается вызов функции которую,
например, передали параметром в другую функцию, то всё нужно
сделать в рантайме...

В целом конечно один условный переход, да еще и с условием которое вычисляется
в нативном коде, существенно не повлияет на производительность.
Если ничего лучше не придумаю, то так и сделаю.
Но мой внутренний перфекционист противится :)

Аватара пользователя
Uvadzucumi
Сообщения: 365
Зарегистрирован: 29 ноя 2011, 07:13
Откуда: Дубай, ОАЭ (Минск, Беларусь)
Контактная информация:

Re: почти пазл

Сообщение Uvadzucumi » 10 июл 2014, 19:48

Немного не о теме проблемы, но в стек нужно и имена переменных ложить, раз уж именованные параметры пошли. Т.е. IMHO должно правильно работать и такое:

Код: Выделить всё

func(a, b){ return a-b}

func(b=>3, a=>2)
Ну и автоматом решиться проблема с пропуском переменных
Меня окружали милые, добрые люди... медленно сжимая кольцо

Аватара пользователя
Xecutor
Мастер
Сообщения: 758
Зарегистрирован: 25 мар 2008, 08:32

Re: почти пазл

Сообщение Xecutor » 11 июл 2014, 05:16

Uvadzucumi писал(а):Немного не о теме проблемы, но в стек нужно и имена переменных ложить, раз уж именованные параметры пошли. Т.е. IMHO должно правильно работать и такое:

Код: Выделить всё

func(a, b){ return a-b}

func(b=>3, a=>2)
Ну и автоматом решиться проблема с пропуском переменных
В статически типизированных языках никаких проблем. Всё считается в точке вызова.
В динамически типизированных нужна магия при подготовке вызова :)

Аватара пользователя
Xecutor
Мастер
Сообщения: 758
Зарегистрирован: 25 мар 2008, 08:32

Re: почти пазл

Сообщение Xecutor » 11 июл 2014, 07:02

Что бы проверить влияние проверки просто воткнул в код вычисляющий значение необязательного
параметра тупой jump на следующую инструкцию.

Вот этот код:

Код: Выделить всё

func f(a,b,c,d=1,e=2,f=3)
  return a+b+c+d+e+f
end

x=0
for i in 1..10000000
  x+=f(1,2,3)
end
print(x)
отрабатывает на 80 милисек быстрее без джампа :(
грубо говоря 2 секунды против 1.920мс
4% однако... с одной стороны тут функция тривиальная.
Если туда хоть чуть-чуть мяса добавить, разница будет статистически незначительной.

Ответить

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 55 гостей