[Программирование, Компиляторы] Почему я прекратил разработку своего старого языка программирования и создал новый
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В этой статье рассказывается о том почему я бросил разработку своего прошлого языка программирования, как новый язык решает проблемы старого, а также показываются тесты программ, написанных на новом языке.Приветствую, читатель. В одной из своих прошлых статей (Как я 12 лет создавал свой ЯП и компилятор к нему) я рассказал о том, как я создавал свой язык программирования, продолжая его развитие и уже почти выпустив версию 0.2, я понял, что это не тот язык, на котором я хочу писать и я начал обдумывание нового языка. Поскольку это мой где-то 8-й по счёту язык программирования, я задумался "А в чём причина того, что я создал уже столько языков, но никак не могу получить тот, который мне подходит?".Когда проектируешь язык программирования, всё время приходится идти на компромиссы, добавляешь идею в язык, затем вторую, а затем когда добавляешь третью, то она идеально сочетается и с первой и со второй, но не с двумя одновременно, и приходится выбирать какую комбинацию из идей использовать. Я решил полностью абстрагироваться от различных идей, наличие которых я считал обязательным для языка которым я хотел бы пользоваться, и заново посмотреть на то, что мне действительно нужно. Вот список идей, которые я считал обязательными для языка:
- обобщённое программирование (хороший пример - Haskell, плохой пример - C)
- язык должен хорошо подходить для того, что бы легко было создавать компилятор, создающий производительные приложения (хороший пример - С, плохой пример - Haskell)
- наличие обобщённых типов
- статическая типизация данных
- возможность особо не думать об одних данных во время работы с другими данными (как например в Haskell не нужно думать о том, что изменение одних данных повлияет на другие, поскольку все данные иммутабельны, или как в моём прошлом языке, если одни и те-же данные находятся в 2-х объектах, то при попытке их изменить, для объекта через который данные меняются, создаётся копия этих данных)
В результате размышлений, вот к каким выводам, по каждому из пунктов, я пришёл:
- обобщённое программирование
Я не готов отказаться от этой идеи. Писать и использовать библиотеки, написанные таким образом, является для меня неотъемлемой частью программирования.
- язык должен хорошо подходить для того, что бы легко было создавать компилятор, создающий производительные приложения
Когда я начинал создавать языки, мне было 19 лет и в этом возрасте мне хотелось, что бы всё было "быстрее, выше, сильнее", хотя я давно вышел из этого возраста, почему то в процессе создания языков, я всё ещё ориентировался на то, что приложения должны иметь крайне высокую производительность. Сейчас же я считаю, что приложения должны иметь достаточную для решаемых задач производительность, а в языке должна быть возможность оптимизации кода, если его производительность оказалась недостаточной. В процессе создания языков, я сотни раз сталкивался с ситуацией, когда мне хотелось реализовать в языке какую либо идею, но я понимал, что её реализация ударит по производительности и я отказывался от идеи, сейчас же я бы принял другое решение во многих подобных ситуации.
- наличие обобщённых типов
Мне очень нравятся обобщённые типы и среди всех виданных мною идей обобщённого программирования, это одна из самых любимых. Во всех моих языках были обобщённые типы (в том или ином виде), но во время проектирования своих языков, я периодически сталкивался с ситуацией, когда хочешь реализовать некую интересную идею, но она не совместима с обобщёнными типами и приходилось от этой идеи отказываться. Я до последнего хотел оставить обобщённые типы в языке, но когда уже стали вырисовываться черты нового языка, я понял, что обобщённым типам в нём не место. Отказ от обобщённых типов, было самым тяжёлым решение в процессе проектирования языка.
- статическая типизация данных
Мне нравится, когда язык немного (именно немного) урезает возможности разработчика, что бы код был безопаснее и во время компиляции можно было находить ошибки. Один из таких способов - статическая типизация данных. В моём прошлом языке для аргументов не было обязательным указывать конкретный тип, а можно было лишь описать то, какое поведение он должен иметь, иногда такому поведению мог соответствовать лишь один тип, иногда несколько, а иногда все типы. В новом языке я решил оставить подобный подход, но во первых упростить его, поскольку описания поведения, было достаточно бойлерплейтными, а во вторых разрешить переменным и результатам функций иметь объекты не конкретного типа, а любого типа который соответствует некому поведению, но такую типизацию уже не назовёшь статической, поскольку за переменной не закреплён конкретный тип, но и назвать это динамической типизацией - язык не поворачивается, поскольку за переменной всё таки закрепляется некая информация о типе, да и проверка типов во время компиляции, а так же оптимизации на основании типа - имеются. Получилось, что-то между статической и динамической типизацией, но всё же ближе к динамической типизации, поэтому можно сказать, что я изменил свой взгляд на то, какая типизация мне больше подходит.
- возможность особо не думать об одних данных во время работы с другими данными
То как это было реализовано в прошлом моём языке, было крайне удобно и не создавало никаких проблем и конфликтов, а лишь наоборот, предоставляло преимущества там, где я этого не ожидал. Поэтому эта идея остаётся и в новом языке, с такой же реализацией.
Я уже написал, что мой предыдущий язык мне не подходит, но какие именно с ним проблемы и как новый язык их решает?Проблема 1: в языке мне не хватает ООП
Проблема 2: требование групп не проверяются во время компиляцииПодробности
Для начала, для тех кто не читал статью про мой предыдущий язык, я объясню что такое группы. Группа - это абстракция над совокупностью типов имеющих схожее поведение. Больше всего группы напоминают классы типов из Haskell, если вы не знакомы с Haskell, то во многих языках есть интерфейсы, интерфейсы и группы похожи, но это не тоже самое. Группа может декларировать некоторое поведение и все члены данной группы должны его реализовывать, но в предыдущем моём языке это поведение записывалось в комментариях к группе, а за тем корректно ли тип реализовывает данное поведение, должен был следить разработчик и компилятор в этом никак не помогал. Я не употребляют алкогольные напитки, но такое ощущение, что когда я принимал решение о добавлении таких групп в свой язык, я был сильно пьян.Решение
В новом языке в группах можно указать функции, которые членам группы нужно реализовать при добавлении в группу и только если какое либо поведение нельзя реализовать с помощью функций, только тогда нужно указывать поведение в комментариях к группе. Компилятор теперь контролирует то, реализовывает ли тип, функции группы и правильно ли он это делает. Если для членов группы можно написать реализацию некой функции, то такая возможность есть, а если для какого либо типа эту функцию нужно изменить (переопределить), то это так же можно сделать. Также в группе можно указать, что все её члены должны быть и членами других групп, такие группы называются - субгруппами.
Возможно у вас возник вопрос: "Проблема 2 решена, а при чём здесь ООП?".
Отвечаю: группы можно представить как классы, функции групп как методы, а субгруппы как родителей текущего класса. Сразу оговорюсь, что определять и переопределять функции из группы, можно только для типов, поэтому при наследовании дочерний классы не может переопределить методы родителя из-за чего при компиляции не может возникнуть проблемы ромба, но конечно на логическом уровне она может возникнуть.Проблема 3: мне не нравится, что данными из модулей можно манипулироватьПодробности
В предыдущем языке поведение импортируемых модулей можно сильно изменить даже с помощью другого импортируемого модуля, в некоторых случаях это прикольно, но огромная проблема для надёжности приложений.Решение
В новом языке такие возможности отсутствуют.Проблема 4: мне не хватало гибкости языкаПодробности
В предыдущем языке очень часто при создании какой либо новой возможности, мне приходилось создавать встроенные функции в компиляторе, хотя в других языках для этого достаточно возможностей языка.РешениеСистема типов в новом языке совместно с тем, что пользовательские типы объявленные через ключевое слово type, всегда являются гетерогенным массивом - решение проблемы.Проблема 5: работа с ошибкамиПодробностиМне нравится, когда работа с ошибками ведется с помощью оператора try catch, но в предыдущем языке ошибка возвращалась в качестве результата функции, либо приводила к закрытию приложения. Такой подход не удобен, но это один из тех случаев в котором я жертвовал удобством ради производительности.Решение
В новом языке работа с ошибка ведется с помощью оператора try catch, а так же если произошло исключение, но его ни кто не ловит, то после вывода сообщения об ошибке, будет выведен полный стек вызова функций до появления исключения.Проблема 6: я постоянно упираюсь в особенности языка C, хочется вернуться на LLVM IRПодробности
Мой первый язык компилировался в ассемблер (FASM), затем я перешёл с ассемблера на LLVM IR, а затем на C. После перехода на C всё стало гораздо проще, но я не хотел привязываться к какому либо компилятору и поэтому решил ориентироваться на стандарт C, что бы можно было компилировать код разными компиляторами (например debug версию tcc, а release версию gcc) и под разные платформы.
Знаете чему равен результат выражения sizeof(bool)? Неизвестно! Потому, что размер bool не определен и если ваш код думает что размер bool равен 1 байт, то ваш код содержит ошибку. Тоже касается и char, int, long. В C есть типы int8_t, int16_t и т.д., но они никак не помогают, поскольку все функции из стандартной библиотеки используют char, int, long, size_t и т.д., однажды в стандартном модуле я нашёл код, который корректно работала на 64-битной архитектуре процессора, но выдавал бы ошибку на 32-битной, эта ошибка была связанна как раз с тем, что размеры типов не определены.
Так же я хотел оптимизировать стандартный модуль и для этого я думал использовать различные расширения языка из gcc и clang, определяя можно ли эти расширения использовать с помощью макросов и если нельзя, то использовать альтернативный способ. Например я хотел использовать SIMD вектора, но в gcc и clang они ужасно мало функциональны по сравнению с LLVM IR, как альтернативу можно использовать immintrin.h, но это в 100 раз менее удобно и только для x86. Как итог, считаю переход с LLVM IR на C - ошибкой.Решение
В новом языке вернулся к LLVM IR.Проблема 7: компиляция и проверка на ошибки модулейПодробности
В предыдущем языке модули не компилировались в C и не проверялись на ошибки, а просто исходный код модуля упаковывался в один файл. Это был связанно с тем, что язык был чрезмерно абстрактным и многое было понятно только при компиляции конечного приложения, поэтому технически не было возможности компиляции модулей в код на C и проверки их на ошибки.Решение
В новом языке нет помех для компиляции модулей. Модули компилируются в LLVM IR с некоторыми пометками, необходимыми для конечного приложения.Предыдущий язык назывался - Cine. Название было сгенерировано генератором случайных имён и было неудачным, к тому же я произносил его как Кайн, а люди которые мне писали всегда писали его как Сайн. Поэтому над новым названием я думал получше.
Название нового языка - Shar (Шар), в честь богини ночи из вселенной Forgotten Realms. Так же получается красивое названия для компилятора: Shar compiler - sharc (похоже на английское слово shark - акула), а также прикольное расширение файла для модулей: Shar module - sharm.Когда я опубликовал статью о Cine, я указал, что завёл блог в котором буду рассказывать о программировании на Cine, на что мне многие писали, что нужно было подготовить к релизу какой либо документ, по которому можно будет изучить основы. В этот раз я так и сделал. Документ не большой (если открыть в Firefox и нажать печать, показывается 8 страниц), только о самых основах и рассчитан на то, что вы уже имеете навыки программирования на каком либо языке.
Ссылка на документ.
Для просмотра реального кода на Shar вы можете посмотреть исходники стандартного модуля, а также исходники sharc, который уже написан на Shar.
Ссылка на исходники стандартного модуля.
Ссылка на исходники sharc.Как я уже писал выше, в новом языке я не особо заботился о производительности и по этому нужно посмотреть на сколько всё плохо. Для тестирования я буду использовать бенчмарк n-body, с сайта The Computer LanguageBenchmarks Game, хочу сразу обратить ваше внимание, что этот тест синтетическая-числодробилка, реальные приложения будут работать значительно лучше (например компилятор sharc, который уже написан на Shar, работает быстрее чем я сам того ожидал). Для информативности, я буду сравнивать производительность с производительностью программы написанной на C, Python, а также просто ради интереса укажу производительность реализации на Cine (заново замерять не буду, а результат возьму с блога про Cine, там же и исходники (ссылка)), все программы не оптимизированы. Замеры производились трижды и брался тот результат, который не самый быстрый, но и не самый медленный.Код на Shar
module Main
const pi Real = 3.141592653589793
const solarMass Real = 4.0 * const::pi * const::pi
const daysPerYear Real = 365.24
const bodies Array = {
[body(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, const::solarMass),
body(4.84143144246472090e+00, -1.16032004402742839e+00, -1.03622044471123109e-01, 1.66007664274403694e-03 * const::daysPerYear, 7.69901118419740425e-03 * const::daysPerYear, -6.90460016972063023e-05 * const::daysPerYear, 9.54791938424326609e-04 * const::solarMass),
body(8.34336671824457987e+00, 4.12479856412430479e+00, -4.03523417114321381e-01, -2.76742510726862411e-03 * const::daysPerYear, 4.99852801234917238e-03 * const::daysPerYear, 2.30417297573763929e-05 * const::daysPerYear, 2.85885980666130812e-04 * const::solarMass),
body(1.28943695621391310e+01, -1.51111514016986312e+01, -2.23307578892655734e-01, 2.96460137564761618e-03 * const::daysPerYear, 2.37847173959480950e-03 * const::daysPerYear, -2.96589568540237556e-05 * const::daysPerYear, 4.36624404335156298e-05 * const::solarMass),
body(1.53796971148509165e+01, -2.59193146099879641e+01, 1.79258772950371181e-01, 2.68067772490389322e-03 * const::daysPerYear, 1.62824170038242295e-03 * const::daysPerYear, -9.51592254519715870e-05 * const::daysPerYear, 5.15138902046611451e-05 * const::solarMass)]
}
type Body
// x Real
// y Real
// z Real
// vx Real
// vy Real
// vz Real
// mass Real
#alwaysinline nothrow
def x~(body Body) Real
return body.typeGetItem(0)
#alwaysinline nothrow
def x`(write body Body, new Real) Real
return body.typePut(0, new)
#alwaysinline nothrow
def y~(body Body) Real
return body.typeGetItem(1)
#alwaysinline nothrow
def y`(write body Body, new Real) Real
return body.typePut(1, new)
#alwaysinline nothrow
def z~(body Body) Real
return body.typeGetItem(2)
#alwaysinline nothrow
def z`(write body Body, new Real) Real
return body.typePut(2, new)
#alwaysinline nothrow
def vx~(body Body) Real
return body.typeGetItem(3)
#alwaysinline nothrow
def vx`(write body Body, new Real) Real
return body.typePut(3, new)
#alwaysinline nothrow
def vy~(body Body) Real
return body.typeGetItem(4)
#alwaysinline nothrow
def vy`(write body Body, new Real) Real
return body.typePut(4, new)
#alwaysinline nothrow
def vz~(body Body) Real
return body.typeGetItem(5)
#alwaysinline nothrow
def vz`(write body Body, new Real) Real
return body.typePut(5, new)
#alwaysinline nothrow
def mass~(body Body) Real
return body.typeGetItem(6)
#alwaysinline nothrow
def mass`(write body Body, new Real) Real
return body.typePut(6, new)
#alwaysinline nothrow
def body(x, y, z, vx, vy, vz, mass Real) Body
return Body.fromList({x, y, z, vx, vy, vz, mass})
#alwaysinline nothrow
def advance(write bodies Array, dt Real)
const length Int = bodies.length~()
for :(i Int = 0) i < length - 1; i++
var b Body = bodies.put(i, Body.fromList({}))
for :(j Int = i + 1) j < length; j++
var b2 Body = bodies.put(j, Body.fromList({}))
const dx Real = b.x~() - b2.x~()
const dy Real = b.y~() - b2.y~()
const dz Real = b.z~() - b2.z~()
const distance Real = sqrt(dx * dx + dy * dy + dz * dz)
const mag Real = dt / (distance * distance * distance)
b.vx`(b.vx~() - dx * b2.mass~() * mag)
b.vy`(b.vy~() - dy * b2.mass~() * mag)
b.vz`(b.vz~() - dz * b2.mass~() * mag)
b2.vx`(b2.vx~() + dx * b.mass~() * mag)
b2.vy`(b2.vy~() + dy * b.mass~() * mag)
b2.vz`(b2.vz~() + dz * b.mass~() * mag)
bodies.put(j, b2).type!(Body)
bodies.put(i, b).type!(Body)
for :(i Int = 0) i < length; i++
var b Body = bodies.put(i, Body.fromList({}))
b.x`(b.x~() + dt * b.vx~())
b.y`(b.y~() + dt * b.vy~())
b.z`(b.z~() + dt * b.vz~())
bodies.put(i, b).type!(Body)
#alwaysinline nothrow
def energy(bodies Array) Real
const length Int = bodies.length~()
var e Real = 0.0
for :(i Int = 0) i < length; i++
var b Body = bodies[i]
e += 0.5 * b.mass~() * (b.vx~() * b.vx~() + b.vy~() * b.vy~() + b.vz~() * b.vz~())
for :(j Int = i + 1) j < length; j++
var b2 Body = bodies[j]
const dx Real = b.x~() - b2.x~()
const dy Real = b.y~() - b2.y~()
const dz Real = b.z~() - b2.z~()
const distance Real = sqrt(dx * dx + dy * dy + dz * dz)
e -= (b.mass~() * b2.mass~()) / distance
return e
#alwaysinline nothrow
def offsetMomentum(write bodies Array)
const length Int = bodies.length~()
var px Real = 0.0
var py Real = 0.0
var pz Real = 0.0
for :(i Int = 0) i < length; i++
px += bodies[i].vx~() * bodies[i].mass~()
py += bodies[i].vy~() * bodies[i].mass~()
pz += bodies[i].vz~() * bodies[i].mass~()
var body0 Body = bodies.put(0, Body.fromList({}))
body0.vx`(!px / const::solarMass)
body0.vy`(!py / const::solarMass)
body0.vz`(!pz / const::solarMass)
bodies.put(0, body0)
def main()
var bodies Array = const::bodies
const n Int = Int.fromString(getCMDLineArgument(1))
bodies.offsetMomentum()
bodies.energy().println()
for :(i Int = 0) i < n; i++
bodies.advance(0.01)
bodies.energy().println()
Код на C
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#define pi 3.141592653589793
#define solar_mass (4 * pi * pi)
#define days_per_year 365.24
struct planet {
double x, y, z;
double vx, vy, vz;
double mass;
};
void advance(int nbodies, struct planet * bodies, double dt)
{
int i, j;
for (i = 0; i < nbodies; i++) {
struct planet * b = &(bodies[i]);
for (j = i + 1; j < nbodies; j++) {
struct planet * b2 = &(bodies[j]);
double dx = b->x - b2->x;
double dy = b->y - b2->y;
double dz = b->z - b2->z;
double distance = sqrt(dx * dx + dy * dy + dz * dz);
double mag = dt / (distance * distance * distance);
b->vx -= dx * b2->mass * mag;
b->vy -= dy * b2->mass * mag;
b->vz -= dz * b2->mass * mag;
b2->vx += dx * b->mass * mag;
b2->vy += dy * b->mass * mag;
b2->vz += dz * b->mass * mag;
}
}
for (i = 0; i < nbodies; i++) {
struct planet * b = &(bodies[i]);
b->x += dt * b->vx;
b->y += dt * b->vy;
b->z += dt * b->vz;
}
}
double energy(int nbodies, struct planet * bodies)
{
double e;
int i, j;
e = 0.0;
for (i = 0; i < nbodies; i++) {
struct planet * b = &(bodies[i]);
e += 0.5 * b->mass * (b->vx * b->vx + b->vy * b->vy + b->vz * b->vz);
for (j = i + 1; j < nbodies; j++) {
struct planet * b2 = &(bodies[j]);
double dx = b->x - b2->x;
double dy = b->y - b2->y;
double dz = b->z - b2->z;
double distance = sqrt(dx * dx + dy * dy + dz * dz);
e -= (b->mass * b2->mass) / distance;
}
}
return e;
}
void offset_momentum(int nbodies, struct planet * bodies)
{
double px = 0.0, py = 0.0, pz = 0.0;
int i;
for (i = 0; i < nbodies; i++) {
px += bodies[i].vx * bodies[i].mass;
py += bodies[i].vy * bodies[i].mass;
pz += bodies[i].vz * bodies[i].mass;
}
bodies[0].vx = - px / solar_mass;
bodies[0].vy = - py / solar_mass;
bodies[0].vz = - pz / solar_mass;
}
#define NBODIES 5
struct planet bodies[NBODIES] = {
{ /* sun */
0, 0, 0, 0, 0, 0, solar_mass
},
{ /* jupiter */
4.84143144246472090e+00,
-1.16032004402742839e+00,
-1.03622044471123109e-01,
1.66007664274403694e-03 * days_per_year,
7.69901118419740425e-03 * days_per_year,
-6.90460016972063023e-05 * days_per_year,
9.54791938424326609e-04 * solar_mass
},
{ /* saturn */
8.34336671824457987e+00,
4.12479856412430479e+00,
-4.03523417114321381e-01,
-2.76742510726862411e-03 * days_per_year,
4.99852801234917238e-03 * days_per_year,
2.30417297573763929e-05 * days_per_year,
2.85885980666130812e-04 * solar_mass
},
{ /* uranus */
1.28943695621391310e+01,
-1.51111514016986312e+01,
-2.23307578892655734e-01,
2.96460137564761618e-03 * days_per_year,
2.37847173959480950e-03 * days_per_year,
-2.96589568540237556e-05 * days_per_year,
4.36624404335156298e-05 * solar_mass
},
{ /* neptune */
1.53796971148509165e+01,
-2.59193146099879641e+01,
1.79258772950371181e-01,
2.68067772490389322e-03 * days_per_year,
1.62824170038242295e-03 * days_per_year,
-9.51592254519715870e-05 * days_per_year,
5.15138902046611451e-05 * solar_mass
}
};
int main(int argc, char ** argv)
{
int n = atoi(argv[1]);
int i;
offset_momentum(NBODIES, bodies);
printf ("%.9f\n", energy(NBODIES, bodies));
for (i = 1; i <= n; i++)
advance(NBODIES, bodies, 0.01);
printf ("%.9f\n", energy(NBODIES, bodies));
return 0;
}
Код на Python
import sys
def combinations(l):
result = []
for x in range(len(l) - 1):
ls = l[x+1:]
for y in ls:
result.append((l[x],y))
return result
PI = 3.14159265358979323
SOLAR_MASS = 4 * PI * PI
DAYS_PER_YEAR = 365.24
BODIES = {
'sun': ([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], SOLAR_MASS),
'jupiter': ([4.84143144246472090e+00,
-1.16032004402742839e+00,
-1.03622044471123109e-01],
[1.66007664274403694e-03 * DAYS_PER_YEAR,
7.69901118419740425e-03 * DAYS_PER_YEAR,
-6.90460016972063023e-05 * DAYS_PER_YEAR],
9.54791938424326609e-04 * SOLAR_MASS),
'saturn': ([8.34336671824457987e+00,
4.12479856412430479e+00,
-4.03523417114321381e-01],
[-2.76742510726862411e-03 * DAYS_PER_YEAR,
4.99852801234917238e-03 * DAYS_PER_YEAR,
2.30417297573763929e-05 * DAYS_PER_YEAR],
2.85885980666130812e-04 * SOLAR_MASS),
'uranus': ([1.28943695621391310e+01,
-1.51111514016986312e+01,
-2.23307578892655734e-01],
[2.96460137564761618e-03 * DAYS_PER_YEAR,
2.37847173959480950e-03 * DAYS_PER_YEAR,
-2.96589568540237556e-05 * DAYS_PER_YEAR],
4.36624404335156298e-05 * SOLAR_MASS),
'neptune': ([1.53796971148509165e+01,
-2.59193146099879641e+01,
1.79258772950371181e-01],
[2.68067772490389322e-03 * DAYS_PER_YEAR,
1.62824170038242295e-03 * DAYS_PER_YEAR,
-9.51592254519715870e-05 * DAYS_PER_YEAR],
5.15138902046611451e-05 * SOLAR_MASS) }
SYSTEM = list(BODIES.values())
PAIRS = combinations(SYSTEM)
def advance(dt, n, bodies=SYSTEM, pairs=PAIRS):
for i in range(n):
for (([x1, y1, z1], v1, m1),
([x2, y2, z2], v2, m2)) in pairs:
dx = x1 - x2
dy = y1 - y2
dz = z1 - z2
mag = dt * ((dx * dx + dy * dy + dz * dz) ** (-1.5))
b1m = m1 * mag
b2m = m2 * mag
v1[0] -= dx * b2m
v1[1] -= dy * b2m
v1[2] -= dz * b2m
v2[0] += dx * b1m
v2[1] += dy * b1m
v2[2] += dz * b1m
for (r, [vx, vy, vz], m) in bodies:
r[0] += dt * vx
r[1] += dt * vy
r[2] += dt * vz
def report_energy(bodies=SYSTEM, pairs=PAIRS, e=0.0):
for (((x1, y1, z1), v1, m1),
((x2, y2, z2), v2, m2)) in pairs:
dx = x1 - x2
dy = y1 - y2
dz = z1 - z2
e -= (m1 * m2) / ((dx * dx + dy * dy + dz * dz) ** 0.5)
for (r, [vx, vy, vz], m) in bodies:
e += m * (vx * vx + vy * vy + vz * vz) / 2.
print("%.9f" % e)
def offset_momentum(ref, bodies=SYSTEM, px=0.0, py=0.0, pz=0.0):
for (r, [vx, vy, vz], m) in bodies:
px -= vx * m
py -= vy * m
pz -= vz * m
(r, v, m) = ref
v[0] = px / m
v[1] = py / m
v[2] = pz / m
def main(n, ref='sun'):
offset_momentum(BODIES[ref])
report_energy()
advance(0.01, n)
report_energy()
if __name__ == '__main__':
main(int(sys.argv[1]))
Результаты:ЯзыкСпособ компиляцииВремя сек.(меньше лучше)Sharsharc -o n-body.ll -m /usr/include/shar/STD.sharm -s main.shar
opt -O3 --mattr=+avx2 n-body.ll -o n-body.bc
clang -O1 -march=native -lm -ldl -pthread n-body.bc /usr/lib/libshar-os-api.so -o n-body42.669Cclang -march=x86-64 -O3 -lm3.966Cclang -march=native -O3 -lm3.698Pythonpython -OO415.093Cinegcc -lm -O3 -march=native6.32Так же я писал, что должна быть возможность оптимизировать программы, поэтому для демонстрации данной возможности, я оптимизирую данную программу, но сразу уточню, что мне не хотелось долго возиться с оптимизацией, поэтому я не оптимизировал программу по самое не балуй, при желании, это программу можно ещё сильнее оптимизировать. Так же я уточню, что оптимизация честная и все полезные расчёты из не оптимизированной программы, происходят и в оптимизированной, никаких предрасчётов или переходов от чисел с плавающей точкой двойной точности к числам с плавающей точкой одинарной точности (этим грешат некоторые оптимизированные версии с The Computer LanguageBenchmarks Game). Способ компиляции такой же.Оптимизированный код на Shar
module Main
const pi Real = 3.141592653589793
const solarMass Real = 4.0 * const::pi * const::pi
const daysPerYear Real = 365.24
const bodies Array = {
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, const::solarMass,
4.84143144246472090e+00, -1.16032004402742839e+00, -1.03622044471123109e-01, 1.66007664274403694e-03 * const::daysPerYear, 7.69901118419740425e-03 * const::daysPerYear, -6.90460016972063023e-05 * const::daysPerYear, 9.54791938424326609e-04 * const::solarMass,
8.34336671824457987e+00, 4.12479856412430479e+00, -4.03523417114321381e-01, -2.76742510726862411e-03 * const::daysPerYear, 4.99852801234917238e-03 * const::daysPerYear, 2.30417297573763929e-05 * const::daysPerYear, 2.85885980666130812e-04 * const::solarMass,
1.28943695621391310e+01, -1.51111514016986312e+01, -2.23307578892655734e-01, 2.96460137564761618e-03 * const::daysPerYear, 2.37847173959480950e-03 * const::daysPerYear, -2.96589568540237556e-05 * const::daysPerYear, 4.36624404335156298e-05 * const::solarMass,
1.53796971148509165e+01, -2.59193146099879641e+01, 1.79258772950371181e-01, 2.68067772490389322e-03 * const::daysPerYear, 1.62824170038242295e-03 * const::daysPerYear, -9.51592254519715870e-05 * const::daysPerYear, 5.15138902046611451e-05 * const::solarMass]
}
#nothrow
def toAlignedArray(bodies Int) Int
const alignedMem Int = getAlignedMem()
for :(i Int = 0) i < 5; i++
for :(j Int = 0) j < 3; j++
alignedMem.unsafe_setI64(i * 8 + j, bodies.unsafe_getI64(i * 7 + j))
for :(j Int = 3) j < 7; j++
alignedMem.unsafe_setI64(i * 8 + j + 1, bodies.unsafe_getI64(i * 7 + j))
return alignedMem
#nothrow
def getAlignedMem() Int
llvm
%##nreg##result pointer## = getelementptr [40 x double], [40 x double]* ##llvmconst##>private unnamed_addr global [40 x double] zeroinitializer, align 32<##, i64 0, i32 0
%##nreg##result i64## = ptrtoint double* %##reg##result pointer## to i64
%##nreg##result## = insertvalue [2 x i64] [i64 ##tnum##STD::Int##, i64 0], i64 %##reg##result i64##, 1
ret [2 x i64] %##reg##result##
#nothrow
def offsetMomentum(bodies Int)
var px Real = 0.0
var py Real = 0.0
var pz Real = 0.0
for :(i Int = 0) i < 5; i++
const mass Real = bodies.unsafe_getI64(i * 8 + 7).intAsReal()
px += bodies.unsafe_getI64(i * 8 + 4).intAsReal() * mass
py += bodies.unsafe_getI64(i * 8 + 5).intAsReal() * mass
pz += bodies.unsafe_getI64(i * 8 + 6).intAsReal() * mass
bodies.unsafe_setI64(4, (!px / const::solarMass).realAsInt())
bodies.unsafe_setI64(5, (!py / const::solarMass).realAsInt())
bodies.unsafe_setI64(6, (!pz / const::solarMass).realAsInt())
#nothrow
def energy(bodies Int) Real
var e Real = 0.0
for :(i Int = 0) i < 5; i++
const x1 Real = bodies.unsafe_getI64(i * 8).intAsReal()
const y1 Real = bodies.unsafe_getI64(i * 8 + 1).intAsReal()
const z1 Real = bodies.unsafe_getI64(i * 8 + 2).intAsReal()
const vx1 Real = bodies.unsafe_getI64(i * 8 + 4).intAsReal()
const vy1 Real = bodies.unsafe_getI64(i * 8 + 5).intAsReal()
const vz1 Real = bodies.unsafe_getI64(i * 8 + 6).intAsReal()
const mass1 Real = bodies.unsafe_getI64(i * 8 + 7).intAsReal()
e += 0.5 * mass1 * (vx1 * vx1 + vy1 * vy1 + vz1 * vz1)
for :(j Int = i + 1) j < 5; j++
const x2 Real = bodies.unsafe_getI64(j * 8).intAsReal()
const y2 Real = bodies.unsafe_getI64(j * 8 + 1).intAsReal()
const z2 Real = bodies.unsafe_getI64(j * 8 + 2).intAsReal()
const mass2 Real = bodies.unsafe_getI64(j * 8 + 7).intAsReal()
const dx Real = x1 - x2
const dy Real = y1 - y2
const dz Real = z1 - z2
const distance Real = sqrt(dx * dx + dy * dy + dz * dz)
e -= (mass1 * mass2) / distance
return e
#alwaysinline nothrow
def intAsReal(int Int) Real
llvm
%##nreg##result## = insertvalue [2 x i64] %0, i64 ##tnum##STD::Real##, 0
ret [2 x i64] %##reg##result##
#alwaysinline nothrow
def realAsInt(real Real) Int
llvm
%##nreg##result## = insertvalue [2 x i64] %0, i64 ##tnum##STD::Int##, 0
ret [2 x i64] %##reg##result##
#alwaysinline nothrow
def advance(bodies Int, dt Real)
llvm
br label %##reg##start##
##nreg##start##:
%##nreg##bodies i64## = extractvalue [2 x i64] %0, 1
%##nreg##bodies## = inttoptr i64 %##reg##bodies i64## to <4 x double>*
%##nreg##dt i64## = extractvalue [2 x i64] %1, 1
%##nreg##dt## = bitcast i64 %##reg##dt i64## to double
br label %##reg##loop 1##
##nreg##loop 1##:
%##nreg##b1 x y z pointer## = phi <4 x double>* [%##reg##bodies##, %##reg##start##], [%##reg##b2 first x y z pointer##, %##reg##end loop 2##]
%##nreg##counter 1## = phi i8 [4, %##reg##start##], [%##reg##counter 1 - 1##, %##reg##end loop 2##]
%##nreg##b1 vx vy vz mass pointer## = getelementptr <4 x double>, <4 x double>* %##reg##b1 x y z pointer##, i64 1
%##nreg##b1 x y z## = load <4 x double>, <4 x double>* %##reg##b1 x y z pointer##, align 32
%##nreg##b2 first x y z pointer## = getelementptr <4 x double>, <4 x double>* %##reg##b1 vx vy vz mass pointer##, i64 1
br label %##reg##loop 2##
##nreg##loop 2##:
%##nreg##b2 x y z pointer## = phi <4 x double>* [%##reg##b2 first x y z pointer##, %##reg##loop 1##], [%##reg##next b2 pointer##, %##reg##loop 2##]
%##nreg##counter 2## = phi i8 [%##reg##counter 1##, %##reg##loop 1##], [%##reg##counter 2 - 1##, %##reg##loop 2##]
%##nreg##b2 vx vy vz mass pointer## = getelementptr <4 x double>, <4 x double>* %##reg##b2 x y z pointer##, i64 1
%##nreg##b1 vx vy vz mass## = load <4 x double>, <4 x double>* %##reg##b1 vx vy vz mass pointer##, align 32
%##nreg##b2 x y z## = load <4 x double>, <4 x double>* %##reg##b2 x y z pointer##, align 32
%##nreg##b2 vx vy vz mass## = load <4 x double>, <4 x double>* %##reg##b2 vx vy vz mass pointer##, align 32
%##nreg##delta## = fsub <4 x double> %##reg##b1 x y z##, %##reg##b2 x y z##
%##nreg##delta ^ 2## = fmul <4 x double> %##reg##delta##, %##reg##delta##
##llvmdeclare##llvm.vector.reduce.fadd.v4f64##declare double @llvm.vector.reduce.fadd.v4f64(double, <4 x double>)##
%##nreg##distance ^ 2## = call double @llvm.vector.reduce.fadd.v4f64(double 0.0, <4 x double> %##reg##delta ^ 2##)
##llvmdeclare##llvm.sqrt.f64##declare double @llvm.sqrt.f64(double)##
%##nreg##distance## = call double @llvm.sqrt.f64(double %##reg##distance ^ 2##)
%##nreg##distance ^ 3## = fmul double %##reg##distance ^ 2##, %##reg##distance##
%##nreg##mag## = fdiv double %##reg##dt##, %##reg##distance ^ 3##
%##nreg##mag as vec## = insertelement <1 x double> undef, double %##reg##mag##, i32 0
%##nreg##mag vec## = shufflevector <1 x double> %##reg##mag as vec##, <1 x double> undef, <4 x i32> zeroinitializer
%##nreg##b1 mass vec## = shufflevector <4 x double> %##reg##b1 vx vy vz mass##, <4 x double> undef, <4 x i32> <i32 3, i32 3, i32 3, i32 3>
%##nreg##b1 mass * mag vec## = fmul <4 x double> %##reg##b1 mass vec##, %##reg##mag vec##
%##nreg##b2 mass vec## = shufflevector <4 x double> %##reg##b2 vx vy vz mass##, <4 x double> undef, <4 x i32> <i32 3, i32 3, i32 3, i32 3>
%##nreg##b2 mass * mag vec## = fmul <4 x double> %##reg##b2 mass vec##, %##reg##mag vec##
%##nreg##-delta## = fneg <4 x double> %##reg##delta##
##llvmdeclare##llvm.fmuladd.v4f64##declare <4 x double> @llvm.fmuladd.v4f64(<4 x double>, <4 x double>, <4 x double>)##
%##nreg##v1 - d * mass2 * mag## = call <4 x double> @llvm.fmuladd.v4f64(<4 x double> %##reg##b2 mass * mag vec##, <4 x double> %##reg##-delta##, <4 x double> %##reg##b1 vx vy vz mass##)
%##nreg##v2 + d * mass1 * mag## = call <4 x double> @llvm.fmuladd.v4f64(<4 x double> %##reg##b1 mass * mag vec##, <4 x double> %##reg##delta##, <4 x double> %##reg##b2 vx vy vz mass##)
store <4 x double> %##reg##v1 - d * mass2 * mag##, <4 x double>* %##reg##b1 vx vy vz mass pointer##, align 32
store <4 x double> %##reg##v2 + d * mass1 * mag##, <4 x double>* %##reg##b2 vx vy vz mass pointer##, align 32
%##nreg##counter 2 - 1## = sub i8 %##reg##counter 2##, 1
%##nreg##(counter 2 - 1) == 0## = icmp eq i8 %##reg##counter 2 - 1##, 0
%##nreg##next b2 pointer## = getelementptr <4 x double>, <4 x double>* %##reg##b2 vx vy vz mass pointer##, i64 1
br i1 %##reg##(counter 2 - 1) == 0##, label %##reg##end loop 2##, label %##reg##loop 2##
##nreg##end loop 2##:
%##nreg##counter 1 - 1## = sub i8 %##reg##counter 1##, 1
%##nreg##(counter 1 - 1) == 0## = icmp eq i8 %##reg##counter 1 - 1##, 0
br i1 %##reg##(counter 1 - 1) == 0##, label %##reg##end loop 1##, label %##reg##loop 1##
##nreg##end loop 1##:
%##nreg##tmp vec 1## = insertelement <4 x double> undef, double %##reg##dt##, i32 0
%##nreg##tmp vec 2## = insertelement <4 x double> %##reg##tmp vec 1##, double %##reg##dt##, i32 1
%##nreg##tmp vec 3## = insertelement <4 x double> %##reg##tmp vec 2##, double %##reg##dt##, i32 2
%##nreg##dt vec## = insertelement <4 x double> %##reg##tmp vec 3##, double 0.0, i32 3
br label %##reg##loop 3##
##nreg##loop 3##:
%##nreg##b x y z pointer## = phi <4 x double>* [%##reg##bodies##, %##reg##end loop 1##], [%##reg##next b x y z pointer##, %##reg##loop 3##]
%##nreg##counter 3## = phi i8 [5, %##reg##end loop 1##], [%##reg##counter 3 - 1##, %##reg##loop 3##]
%##nreg##b vx vy vz mass pointer## = getelementptr <4 x double>, <4 x double>* %##reg##b x y z pointer##, i64 1
%##nreg##b x y z## = load <4 x double>, <4 x double>* %##reg##b x y z pointer##, align 32
%##nreg##b vx vy vz mass## = load <4 x double>, <4 x double>* %##reg##b vx vy vz mass pointer##, align 32
%##nreg##xyz + dt * v## = call <4 x double> @llvm.fmuladd.v4f64(<4 x double> %##reg##dt vec##, <4 x double> %##reg##b vx vy vz mass##, <4 x double> %##reg##b x y z##)
store <4 x double> %##reg##xyz + dt * v##, <4 x double>* %##reg##b x y z pointer##, align 32
%##nreg##counter 3 - 1## = sub i8 %##reg##counter 3##, 1
%##nreg##(counter 3 - 1) == 0## = icmp eq i8 %##reg##counter 3 - 1##, 0
%##nreg##next b x y z pointer## = getelementptr <4 x double>, <4 x double>* %##reg##b vx vy vz mass pointer##, i64 1
br i1 %##reg##(counter 3 - 1) == 0##, label %##reg##end loop 3##, label %##reg##loop 3##
##nreg##end loop 3##:
ret [2 x i64] zeroinitializer
def main()
const bodies Int = const::bodies.unsafe_offsetI64(3).toAlignedArray()
const n Int = Int.fromString(getCMDLineArgument(1))
bodies.offsetMomentum()
bodies.energy().println()
for :(i Int = 0) i < n; i++
bodies.advance(0.01)
bodies.energy().println()
Результат: 3.034 сек.Итог
Все поставленные задачи были достигнуты, результатом доволен.Сcылки
https://github.com/SharDeveloper/sharc
https://github.com/SharDeveloper/shar-standart-module
https://github.com/SharDeveloper/libshar-os-api_linux-x86_64
https://github.com/SharDeveloper/kate-shar-highlighting
===========
Источник:
habr.com
===========
Похожие новости:
- [Программирование, Анализ и проектирование систем, Визуализация данных] Построение графов с помощью библиотеки PyViz на основе текстового анализа ESG стратегий отдельных компаний
- [Системное администрирование, PHP, MySQL, Программирование, Cisco] Автоматический и автоматизированный способы блокировки ресурсов по поисковой выдаче
- [Разработка веб-сайтов, JavaScript, Программирование] Redux Vs Vuex. Часть 1
- [Программирование, Управление персоналом, Карьера в IT-индустрии, Читальный зал, 1С] Знакомьтесь: Слесарь 1С. Профессия, которая уже захватила рынок
- [Информационная безопасность, Программирование, Производство и разработка электроники, Процессоры] Как ускорить шифрование по ГОСТ 28147-89 на процессоре Baikal-M
- [Программирование, Промышленное программирование, Инженерные системы] Укрощение строптивого или Reverse Engineering французского паллетайзера на Simatic S7-300
- [Python, Программирование, Accessibility, Здоровье] Эксперимент для сотрудника с нарушением слуха, ч. 2, проверка на себе
- [Программирование, Совершенный код, C, Rust, Браузеры] Tor Project планирует заменить код C на Rust
- [Программирование, .NET, C#] Даты, время и часовые пояса: улучшения в .NET 6
- [Информационная безопасность, Программирование, Квантовые технологии] Взлом квантовой программы
Теги для поиска: #_programmirovanie (Программирование), #_kompiljatory (Компиляторы), #_kompiljatory (компиляторы), #_jazyki_programmirovanija (языки программирования), #_llvm, #_llvm_ir, #_optimizatsija_koda (оптимизация кода), #_benchmark (бенчмарк), #_programmirovanie (
Программирование
), #_kompiljatory (
Компиляторы
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 03:14
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В этой статье рассказывается о том почему я бросил разработку своего прошлого языка программирования, как новый язык решает проблемы старого, а также показываются тесты программ, написанных на новом языке.Приветствую, читатель. В одной из своих прошлых статей (Как я 12 лет создавал свой ЯП и компилятор к нему) я рассказал о том, как я создавал свой язык программирования, продолжая его развитие и уже почти выпустив версию 0.2, я понял, что это не тот язык, на котором я хочу писать и я начал обдумывание нового языка. Поскольку это мой где-то 8-й по счёту язык программирования, я задумался "А в чём причина того, что я создал уже столько языков, но никак не могу получить тот, который мне подходит?".Когда проектируешь язык программирования, всё время приходится идти на компромиссы, добавляешь идею в язык, затем вторую, а затем когда добавляешь третью, то она идеально сочетается и с первой и со второй, но не с двумя одновременно, и приходится выбирать какую комбинацию из идей использовать. Я решил полностью абстрагироваться от различных идей, наличие которых я считал обязательным для языка которым я хотел бы пользоваться, и заново посмотреть на то, что мне действительно нужно. Вот список идей, которые я считал обязательными для языка:
Проблема 2: требование групп не проверяются во время компиляцииПодробности Для начала, для тех кто не читал статью про мой предыдущий язык, я объясню что такое группы. Группа - это абстракция над совокупностью типов имеющих схожее поведение. Больше всего группы напоминают классы типов из Haskell, если вы не знакомы с Haskell, то во многих языках есть интерфейсы, интерфейсы и группы похожи, но это не тоже самое. Группа может декларировать некоторое поведение и все члены данной группы должны его реализовывать, но в предыдущем моём языке это поведение записывалось в комментариях к группе, а за тем корректно ли тип реализовывает данное поведение, должен был следить разработчик и компилятор в этом никак не помогал. Я не употребляют алкогольные напитки, но такое ощущение, что когда я принимал решение о добавлении таких групп в свой язык, я был сильно пьян.Решение В новом языке в группах можно указать функции, которые членам группы нужно реализовать при добавлении в группу и только если какое либо поведение нельзя реализовать с помощью функций, только тогда нужно указывать поведение в комментариях к группе. Компилятор теперь контролирует то, реализовывает ли тип, функции группы и правильно ли он это делает. Если для членов группы можно написать реализацию некой функции, то такая возможность есть, а если для какого либо типа эту функцию нужно изменить (переопределить), то это так же можно сделать. Также в группе можно указать, что все её члены должны быть и членами других групп, такие группы называются - субгруппами. Возможно у вас возник вопрос: "Проблема 2 решена, а при чём здесь ООП?". Отвечаю: группы можно представить как классы, функции групп как методы, а субгруппы как родителей текущего класса. Сразу оговорюсь, что определять и переопределять функции из группы, можно только для типов, поэтому при наследовании дочерний классы не может переопределить методы родителя из-за чего при компиляции не может возникнуть проблемы ромба, но конечно на логическом уровне она может возникнуть.Проблема 3: мне не нравится, что данными из модулей можно манипулироватьПодробности В предыдущем языке поведение импортируемых модулей можно сильно изменить даже с помощью другого импортируемого модуля, в некоторых случаях это прикольно, но огромная проблема для надёжности приложений.Решение В новом языке такие возможности отсутствуют.Проблема 4: мне не хватало гибкости языкаПодробности В предыдущем языке очень часто при создании какой либо новой возможности, мне приходилось создавать встроенные функции в компиляторе, хотя в других языках для этого достаточно возможностей языка.РешениеСистема типов в новом языке совместно с тем, что пользовательские типы объявленные через ключевое слово type, всегда являются гетерогенным массивом - решение проблемы.Проблема 5: работа с ошибкамиПодробностиМне нравится, когда работа с ошибками ведется с помощью оператора try catch, но в предыдущем языке ошибка возвращалась в качестве результата функции, либо приводила к закрытию приложения. Такой подход не удобен, но это один из тех случаев в котором я жертвовал удобством ради производительности.Решение В новом языке работа с ошибка ведется с помощью оператора try catch, а так же если произошло исключение, но его ни кто не ловит, то после вывода сообщения об ошибке, будет выведен полный стек вызова функций до появления исключения.Проблема 6: я постоянно упираюсь в особенности языка C, хочется вернуться на LLVM IRПодробности Мой первый язык компилировался в ассемблер (FASM), затем я перешёл с ассемблера на LLVM IR, а затем на C. После перехода на C всё стало гораздо проще, но я не хотел привязываться к какому либо компилятору и поэтому решил ориентироваться на стандарт C, что бы можно было компилировать код разными компиляторами (например debug версию tcc, а release версию gcc) и под разные платформы. Знаете чему равен результат выражения sizeof(bool)? Неизвестно! Потому, что размер bool не определен и если ваш код думает что размер bool равен 1 байт, то ваш код содержит ошибку. Тоже касается и char, int, long. В C есть типы int8_t, int16_t и т.д., но они никак не помогают, поскольку все функции из стандартной библиотеки используют char, int, long, size_t и т.д., однажды в стандартном модуле я нашёл код, который корректно работала на 64-битной архитектуре процессора, но выдавал бы ошибку на 32-битной, эта ошибка была связанна как раз с тем, что размеры типов не определены. Так же я хотел оптимизировать стандартный модуль и для этого я думал использовать различные расширения языка из gcc и clang, определяя можно ли эти расширения использовать с помощью макросов и если нельзя, то использовать альтернативный способ. Например я хотел использовать SIMD вектора, но в gcc и clang они ужасно мало функциональны по сравнению с LLVM IR, как альтернативу можно использовать immintrin.h, но это в 100 раз менее удобно и только для x86. Как итог, считаю переход с LLVM IR на C - ошибкой.Решение В новом языке вернулся к LLVM IR.Проблема 7: компиляция и проверка на ошибки модулейПодробности В предыдущем языке модули не компилировались в C и не проверялись на ошибки, а просто исходный код модуля упаковывался в один файл. Это был связанно с тем, что язык был чрезмерно абстрактным и многое было понятно только при компиляции конечного приложения, поэтому технически не было возможности компиляции модулей в код на C и проверки их на ошибки.Решение В новом языке нет помех для компиляции модулей. Модули компилируются в LLVM IR с некоторыми пометками, необходимыми для конечного приложения.Предыдущий язык назывался - Cine. Название было сгенерировано генератором случайных имён и было неудачным, к тому же я произносил его как Кайн, а люди которые мне писали всегда писали его как Сайн. Поэтому над новым названием я думал получше. Название нового языка - Shar (Шар), в честь богини ночи из вселенной Forgotten Realms. Так же получается красивое названия для компилятора: Shar compiler - sharc (похоже на английское слово shark - акула), а также прикольное расширение файла для модулей: Shar module - sharm.Когда я опубликовал статью о Cine, я указал, что завёл блог в котором буду рассказывать о программировании на Cine, на что мне многие писали, что нужно было подготовить к релизу какой либо документ, по которому можно будет изучить основы. В этот раз я так и сделал. Документ не большой (если открыть в Firefox и нажать печать, показывается 8 страниц), только о самых основах и рассчитан на то, что вы уже имеете навыки программирования на каком либо языке. Ссылка на документ. Для просмотра реального кода на Shar вы можете посмотреть исходники стандартного модуля, а также исходники sharc, который уже написан на Shar. Ссылка на исходники стандартного модуля. Ссылка на исходники sharc.Как я уже писал выше, в новом языке я не особо заботился о производительности и по этому нужно посмотреть на сколько всё плохо. Для тестирования я буду использовать бенчмарк n-body, с сайта The Computer LanguageBenchmarks Game, хочу сразу обратить ваше внимание, что этот тест синтетическая-числодробилка, реальные приложения будут работать значительно лучше (например компилятор sharc, который уже написан на Shar, работает быстрее чем я сам того ожидал). Для информативности, я буду сравнивать производительность с производительностью программы написанной на C, Python, а также просто ради интереса укажу производительность реализации на Cine (заново замерять не буду, а результат возьму с блога про Cine, там же и исходники (ссылка)), все программы не оптимизированы. Замеры производились трижды и брался тот результат, который не самый быстрый, но и не самый медленный.Код на Shar module Main
const pi Real = 3.141592653589793 const solarMass Real = 4.0 * const::pi * const::pi const daysPerYear Real = 365.24 const bodies Array = { [body(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, const::solarMass), body(4.84143144246472090e+00, -1.16032004402742839e+00, -1.03622044471123109e-01, 1.66007664274403694e-03 * const::daysPerYear, 7.69901118419740425e-03 * const::daysPerYear, -6.90460016972063023e-05 * const::daysPerYear, 9.54791938424326609e-04 * const::solarMass), body(8.34336671824457987e+00, 4.12479856412430479e+00, -4.03523417114321381e-01, -2.76742510726862411e-03 * const::daysPerYear, 4.99852801234917238e-03 * const::daysPerYear, 2.30417297573763929e-05 * const::daysPerYear, 2.85885980666130812e-04 * const::solarMass), body(1.28943695621391310e+01, -1.51111514016986312e+01, -2.23307578892655734e-01, 2.96460137564761618e-03 * const::daysPerYear, 2.37847173959480950e-03 * const::daysPerYear, -2.96589568540237556e-05 * const::daysPerYear, 4.36624404335156298e-05 * const::solarMass), body(1.53796971148509165e+01, -2.59193146099879641e+01, 1.79258772950371181e-01, 2.68067772490389322e-03 * const::daysPerYear, 1.62824170038242295e-03 * const::daysPerYear, -9.51592254519715870e-05 * const::daysPerYear, 5.15138902046611451e-05 * const::solarMass)] } type Body // x Real // y Real // z Real // vx Real // vy Real // vz Real // mass Real #alwaysinline nothrow def x~(body Body) Real return body.typeGetItem(0) #alwaysinline nothrow def x`(write body Body, new Real) Real return body.typePut(0, new) #alwaysinline nothrow def y~(body Body) Real return body.typeGetItem(1) #alwaysinline nothrow def y`(write body Body, new Real) Real return body.typePut(1, new) #alwaysinline nothrow def z~(body Body) Real return body.typeGetItem(2) #alwaysinline nothrow def z`(write body Body, new Real) Real return body.typePut(2, new) #alwaysinline nothrow def vx~(body Body) Real return body.typeGetItem(3) #alwaysinline nothrow def vx`(write body Body, new Real) Real return body.typePut(3, new) #alwaysinline nothrow def vy~(body Body) Real return body.typeGetItem(4) #alwaysinline nothrow def vy`(write body Body, new Real) Real return body.typePut(4, new) #alwaysinline nothrow def vz~(body Body) Real return body.typeGetItem(5) #alwaysinline nothrow def vz`(write body Body, new Real) Real return body.typePut(5, new) #alwaysinline nothrow def mass~(body Body) Real return body.typeGetItem(6) #alwaysinline nothrow def mass`(write body Body, new Real) Real return body.typePut(6, new) #alwaysinline nothrow def body(x, y, z, vx, vy, vz, mass Real) Body return Body.fromList({x, y, z, vx, vy, vz, mass}) #alwaysinline nothrow def advance(write bodies Array, dt Real) const length Int = bodies.length~() for :(i Int = 0) i < length - 1; i++ var b Body = bodies.put(i, Body.fromList({})) for :(j Int = i + 1) j < length; j++ var b2 Body = bodies.put(j, Body.fromList({})) const dx Real = b.x~() - b2.x~() const dy Real = b.y~() - b2.y~() const dz Real = b.z~() - b2.z~() const distance Real = sqrt(dx * dx + dy * dy + dz * dz) const mag Real = dt / (distance * distance * distance) b.vx`(b.vx~() - dx * b2.mass~() * mag) b.vy`(b.vy~() - dy * b2.mass~() * mag) b.vz`(b.vz~() - dz * b2.mass~() * mag) b2.vx`(b2.vx~() + dx * b.mass~() * mag) b2.vy`(b2.vy~() + dy * b.mass~() * mag) b2.vz`(b2.vz~() + dz * b.mass~() * mag) bodies.put(j, b2).type!(Body) bodies.put(i, b).type!(Body) for :(i Int = 0) i < length; i++ var b Body = bodies.put(i, Body.fromList({})) b.x`(b.x~() + dt * b.vx~()) b.y`(b.y~() + dt * b.vy~()) b.z`(b.z~() + dt * b.vz~()) bodies.put(i, b).type!(Body) #alwaysinline nothrow def energy(bodies Array) Real const length Int = bodies.length~() var e Real = 0.0 for :(i Int = 0) i < length; i++ var b Body = bodies[i] e += 0.5 * b.mass~() * (b.vx~() * b.vx~() + b.vy~() * b.vy~() + b.vz~() * b.vz~()) for :(j Int = i + 1) j < length; j++ var b2 Body = bodies[j] const dx Real = b.x~() - b2.x~() const dy Real = b.y~() - b2.y~() const dz Real = b.z~() - b2.z~() const distance Real = sqrt(dx * dx + dy * dy + dz * dz) e -= (b.mass~() * b2.mass~()) / distance return e #alwaysinline nothrow def offsetMomentum(write bodies Array) const length Int = bodies.length~() var px Real = 0.0 var py Real = 0.0 var pz Real = 0.0 for :(i Int = 0) i < length; i++ px += bodies[i].vx~() * bodies[i].mass~() py += bodies[i].vy~() * bodies[i].mass~() pz += bodies[i].vz~() * bodies[i].mass~() var body0 Body = bodies.put(0, Body.fromList({})) body0.vx`(!px / const::solarMass) body0.vy`(!py / const::solarMass) body0.vz`(!pz / const::solarMass) bodies.put(0, body0) def main() var bodies Array = const::bodies const n Int = Int.fromString(getCMDLineArgument(1)) bodies.offsetMomentum() bodies.energy().println() for :(i Int = 0) i < n; i++ bodies.advance(0.01) bodies.energy().println() #include <math.h>
#include <stdio.h> #include <stdlib.h> #define pi 3.141592653589793 #define solar_mass (4 * pi * pi) #define days_per_year 365.24 struct planet { double x, y, z; double vx, vy, vz; double mass; }; void advance(int nbodies, struct planet * bodies, double dt) { int i, j; for (i = 0; i < nbodies; i++) { struct planet * b = &(bodies[i]); for (j = i + 1; j < nbodies; j++) { struct planet * b2 = &(bodies[j]); double dx = b->x - b2->x; double dy = b->y - b2->y; double dz = b->z - b2->z; double distance = sqrt(dx * dx + dy * dy + dz * dz); double mag = dt / (distance * distance * distance); b->vx -= dx * b2->mass * mag; b->vy -= dy * b2->mass * mag; b->vz -= dz * b2->mass * mag; b2->vx += dx * b->mass * mag; b2->vy += dy * b->mass * mag; b2->vz += dz * b->mass * mag; } } for (i = 0; i < nbodies; i++) { struct planet * b = &(bodies[i]); b->x += dt * b->vx; b->y += dt * b->vy; b->z += dt * b->vz; } } double energy(int nbodies, struct planet * bodies) { double e; int i, j; e = 0.0; for (i = 0; i < nbodies; i++) { struct planet * b = &(bodies[i]); e += 0.5 * b->mass * (b->vx * b->vx + b->vy * b->vy + b->vz * b->vz); for (j = i + 1; j < nbodies; j++) { struct planet * b2 = &(bodies[j]); double dx = b->x - b2->x; double dy = b->y - b2->y; double dz = b->z - b2->z; double distance = sqrt(dx * dx + dy * dy + dz * dz); e -= (b->mass * b2->mass) / distance; } } return e; } void offset_momentum(int nbodies, struct planet * bodies) { double px = 0.0, py = 0.0, pz = 0.0; int i; for (i = 0; i < nbodies; i++) { px += bodies[i].vx * bodies[i].mass; py += bodies[i].vy * bodies[i].mass; pz += bodies[i].vz * bodies[i].mass; } bodies[0].vx = - px / solar_mass; bodies[0].vy = - py / solar_mass; bodies[0].vz = - pz / solar_mass; } #define NBODIES 5 struct planet bodies[NBODIES] = { { /* sun */ 0, 0, 0, 0, 0, 0, solar_mass }, { /* jupiter */ 4.84143144246472090e+00, -1.16032004402742839e+00, -1.03622044471123109e-01, 1.66007664274403694e-03 * days_per_year, 7.69901118419740425e-03 * days_per_year, -6.90460016972063023e-05 * days_per_year, 9.54791938424326609e-04 * solar_mass }, { /* saturn */ 8.34336671824457987e+00, 4.12479856412430479e+00, -4.03523417114321381e-01, -2.76742510726862411e-03 * days_per_year, 4.99852801234917238e-03 * days_per_year, 2.30417297573763929e-05 * days_per_year, 2.85885980666130812e-04 * solar_mass }, { /* uranus */ 1.28943695621391310e+01, -1.51111514016986312e+01, -2.23307578892655734e-01, 2.96460137564761618e-03 * days_per_year, 2.37847173959480950e-03 * days_per_year, -2.96589568540237556e-05 * days_per_year, 4.36624404335156298e-05 * solar_mass }, { /* neptune */ 1.53796971148509165e+01, -2.59193146099879641e+01, 1.79258772950371181e-01, 2.68067772490389322e-03 * days_per_year, 1.62824170038242295e-03 * days_per_year, -9.51592254519715870e-05 * days_per_year, 5.15138902046611451e-05 * solar_mass } }; int main(int argc, char ** argv) { int n = atoi(argv[1]); int i; offset_momentum(NBODIES, bodies); printf ("%.9f\n", energy(NBODIES, bodies)); for (i = 1; i <= n; i++) advance(NBODIES, bodies, 0.01); printf ("%.9f\n", energy(NBODIES, bodies)); return 0; } import sys
def combinations(l): result = [] for x in range(len(l) - 1): ls = l[x+1:] for y in ls: result.append((l[x],y)) return result PI = 3.14159265358979323 SOLAR_MASS = 4 * PI * PI DAYS_PER_YEAR = 365.24 BODIES = { 'sun': ([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], SOLAR_MASS), 'jupiter': ([4.84143144246472090e+00, -1.16032004402742839e+00, -1.03622044471123109e-01], [1.66007664274403694e-03 * DAYS_PER_YEAR, 7.69901118419740425e-03 * DAYS_PER_YEAR, -6.90460016972063023e-05 * DAYS_PER_YEAR], 9.54791938424326609e-04 * SOLAR_MASS), 'saturn': ([8.34336671824457987e+00, 4.12479856412430479e+00, -4.03523417114321381e-01], [-2.76742510726862411e-03 * DAYS_PER_YEAR, 4.99852801234917238e-03 * DAYS_PER_YEAR, 2.30417297573763929e-05 * DAYS_PER_YEAR], 2.85885980666130812e-04 * SOLAR_MASS), 'uranus': ([1.28943695621391310e+01, -1.51111514016986312e+01, -2.23307578892655734e-01], [2.96460137564761618e-03 * DAYS_PER_YEAR, 2.37847173959480950e-03 * DAYS_PER_YEAR, -2.96589568540237556e-05 * DAYS_PER_YEAR], 4.36624404335156298e-05 * SOLAR_MASS), 'neptune': ([1.53796971148509165e+01, -2.59193146099879641e+01, 1.79258772950371181e-01], [2.68067772490389322e-03 * DAYS_PER_YEAR, 1.62824170038242295e-03 * DAYS_PER_YEAR, -9.51592254519715870e-05 * DAYS_PER_YEAR], 5.15138902046611451e-05 * SOLAR_MASS) } SYSTEM = list(BODIES.values()) PAIRS = combinations(SYSTEM) def advance(dt, n, bodies=SYSTEM, pairs=PAIRS): for i in range(n): for (([x1, y1, z1], v1, m1), ([x2, y2, z2], v2, m2)) in pairs: dx = x1 - x2 dy = y1 - y2 dz = z1 - z2 mag = dt * ((dx * dx + dy * dy + dz * dz) ** (-1.5)) b1m = m1 * mag b2m = m2 * mag v1[0] -= dx * b2m v1[1] -= dy * b2m v1[2] -= dz * b2m v2[0] += dx * b1m v2[1] += dy * b1m v2[2] += dz * b1m for (r, [vx, vy, vz], m) in bodies: r[0] += dt * vx r[1] += dt * vy r[2] += dt * vz def report_energy(bodies=SYSTEM, pairs=PAIRS, e=0.0): for (((x1, y1, z1), v1, m1), ((x2, y2, z2), v2, m2)) in pairs: dx = x1 - x2 dy = y1 - y2 dz = z1 - z2 e -= (m1 * m2) / ((dx * dx + dy * dy + dz * dz) ** 0.5) for (r, [vx, vy, vz], m) in bodies: e += m * (vx * vx + vy * vy + vz * vz) / 2. print("%.9f" % e) def offset_momentum(ref, bodies=SYSTEM, px=0.0, py=0.0, pz=0.0): for (r, [vx, vy, vz], m) in bodies: px -= vx * m py -= vy * m pz -= vz * m (r, v, m) = ref v[0] = px / m v[1] = py / m v[2] = pz / m def main(n, ref='sun'): offset_momentum(BODIES[ref]) report_energy() advance(0.01, n) report_energy() if __name__ == '__main__': main(int(sys.argv[1])) opt -O3 --mattr=+avx2 n-body.ll -o n-body.bc clang -O1 -march=native -lm -ldl -pthread n-body.bc /usr/lib/libshar-os-api.so -o n-body42.669Cclang -march=x86-64 -O3 -lm3.966Cclang -march=native -O3 -lm3.698Pythonpython -OO415.093Cinegcc -lm -O3 -march=native6.32Так же я писал, что должна быть возможность оптимизировать программы, поэтому для демонстрации данной возможности, я оптимизирую данную программу, но сразу уточню, что мне не хотелось долго возиться с оптимизацией, поэтому я не оптимизировал программу по самое не балуй, при желании, это программу можно ещё сильнее оптимизировать. Так же я уточню, что оптимизация честная и все полезные расчёты из не оптимизированной программы, происходят и в оптимизированной, никаких предрасчётов или переходов от чисел с плавающей точкой двойной точности к числам с плавающей точкой одинарной точности (этим грешат некоторые оптимизированные версии с The Computer LanguageBenchmarks Game). Способ компиляции такой же.Оптимизированный код на Shar module Main
const pi Real = 3.141592653589793 const solarMass Real = 4.0 * const::pi * const::pi const daysPerYear Real = 365.24 const bodies Array = { [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, const::solarMass, 4.84143144246472090e+00, -1.16032004402742839e+00, -1.03622044471123109e-01, 1.66007664274403694e-03 * const::daysPerYear, 7.69901118419740425e-03 * const::daysPerYear, -6.90460016972063023e-05 * const::daysPerYear, 9.54791938424326609e-04 * const::solarMass, 8.34336671824457987e+00, 4.12479856412430479e+00, -4.03523417114321381e-01, -2.76742510726862411e-03 * const::daysPerYear, 4.99852801234917238e-03 * const::daysPerYear, 2.30417297573763929e-05 * const::daysPerYear, 2.85885980666130812e-04 * const::solarMass, 1.28943695621391310e+01, -1.51111514016986312e+01, -2.23307578892655734e-01, 2.96460137564761618e-03 * const::daysPerYear, 2.37847173959480950e-03 * const::daysPerYear, -2.96589568540237556e-05 * const::daysPerYear, 4.36624404335156298e-05 * const::solarMass, 1.53796971148509165e+01, -2.59193146099879641e+01, 1.79258772950371181e-01, 2.68067772490389322e-03 * const::daysPerYear, 1.62824170038242295e-03 * const::daysPerYear, -9.51592254519715870e-05 * const::daysPerYear, 5.15138902046611451e-05 * const::solarMass] } #nothrow def toAlignedArray(bodies Int) Int const alignedMem Int = getAlignedMem() for :(i Int = 0) i < 5; i++ for :(j Int = 0) j < 3; j++ alignedMem.unsafe_setI64(i * 8 + j, bodies.unsafe_getI64(i * 7 + j)) for :(j Int = 3) j < 7; j++ alignedMem.unsafe_setI64(i * 8 + j + 1, bodies.unsafe_getI64(i * 7 + j)) return alignedMem #nothrow def getAlignedMem() Int llvm %##nreg##result pointer## = getelementptr [40 x double], [40 x double]* ##llvmconst##>private unnamed_addr global [40 x double] zeroinitializer, align 32<##, i64 0, i32 0 %##nreg##result i64## = ptrtoint double* %##reg##result pointer## to i64 %##nreg##result## = insertvalue [2 x i64] [i64 ##tnum##STD::Int##, i64 0], i64 %##reg##result i64##, 1 ret [2 x i64] %##reg##result## #nothrow def offsetMomentum(bodies Int) var px Real = 0.0 var py Real = 0.0 var pz Real = 0.0 for :(i Int = 0) i < 5; i++ const mass Real = bodies.unsafe_getI64(i * 8 + 7).intAsReal() px += bodies.unsafe_getI64(i * 8 + 4).intAsReal() * mass py += bodies.unsafe_getI64(i * 8 + 5).intAsReal() * mass pz += bodies.unsafe_getI64(i * 8 + 6).intAsReal() * mass bodies.unsafe_setI64(4, (!px / const::solarMass).realAsInt()) bodies.unsafe_setI64(5, (!py / const::solarMass).realAsInt()) bodies.unsafe_setI64(6, (!pz / const::solarMass).realAsInt()) #nothrow def energy(bodies Int) Real var e Real = 0.0 for :(i Int = 0) i < 5; i++ const x1 Real = bodies.unsafe_getI64(i * 8).intAsReal() const y1 Real = bodies.unsafe_getI64(i * 8 + 1).intAsReal() const z1 Real = bodies.unsafe_getI64(i * 8 + 2).intAsReal() const vx1 Real = bodies.unsafe_getI64(i * 8 + 4).intAsReal() const vy1 Real = bodies.unsafe_getI64(i * 8 + 5).intAsReal() const vz1 Real = bodies.unsafe_getI64(i * 8 + 6).intAsReal() const mass1 Real = bodies.unsafe_getI64(i * 8 + 7).intAsReal() e += 0.5 * mass1 * (vx1 * vx1 + vy1 * vy1 + vz1 * vz1) for :(j Int = i + 1) j < 5; j++ const x2 Real = bodies.unsafe_getI64(j * 8).intAsReal() const y2 Real = bodies.unsafe_getI64(j * 8 + 1).intAsReal() const z2 Real = bodies.unsafe_getI64(j * 8 + 2).intAsReal() const mass2 Real = bodies.unsafe_getI64(j * 8 + 7).intAsReal() const dx Real = x1 - x2 const dy Real = y1 - y2 const dz Real = z1 - z2 const distance Real = sqrt(dx * dx + dy * dy + dz * dz) e -= (mass1 * mass2) / distance return e #alwaysinline nothrow def intAsReal(int Int) Real llvm %##nreg##result## = insertvalue [2 x i64] %0, i64 ##tnum##STD::Real##, 0 ret [2 x i64] %##reg##result## #alwaysinline nothrow def realAsInt(real Real) Int llvm %##nreg##result## = insertvalue [2 x i64] %0, i64 ##tnum##STD::Int##, 0 ret [2 x i64] %##reg##result## #alwaysinline nothrow def advance(bodies Int, dt Real) llvm br label %##reg##start## ##nreg##start##: %##nreg##bodies i64## = extractvalue [2 x i64] %0, 1 %##nreg##bodies## = inttoptr i64 %##reg##bodies i64## to <4 x double>* %##nreg##dt i64## = extractvalue [2 x i64] %1, 1 %##nreg##dt## = bitcast i64 %##reg##dt i64## to double br label %##reg##loop 1## ##nreg##loop 1##: %##nreg##b1 x y z pointer## = phi <4 x double>* [%##reg##bodies##, %##reg##start##], [%##reg##b2 first x y z pointer##, %##reg##end loop 2##] %##nreg##counter 1## = phi i8 [4, %##reg##start##], [%##reg##counter 1 - 1##, %##reg##end loop 2##] %##nreg##b1 vx vy vz mass pointer## = getelementptr <4 x double>, <4 x double>* %##reg##b1 x y z pointer##, i64 1 %##nreg##b1 x y z## = load <4 x double>, <4 x double>* %##reg##b1 x y z pointer##, align 32 %##nreg##b2 first x y z pointer## = getelementptr <4 x double>, <4 x double>* %##reg##b1 vx vy vz mass pointer##, i64 1 br label %##reg##loop 2## ##nreg##loop 2##: %##nreg##b2 x y z pointer## = phi <4 x double>* [%##reg##b2 first x y z pointer##, %##reg##loop 1##], [%##reg##next b2 pointer##, %##reg##loop 2##] %##nreg##counter 2## = phi i8 [%##reg##counter 1##, %##reg##loop 1##], [%##reg##counter 2 - 1##, %##reg##loop 2##] %##nreg##b2 vx vy vz mass pointer## = getelementptr <4 x double>, <4 x double>* %##reg##b2 x y z pointer##, i64 1 %##nreg##b1 vx vy vz mass## = load <4 x double>, <4 x double>* %##reg##b1 vx vy vz mass pointer##, align 32 %##nreg##b2 x y z## = load <4 x double>, <4 x double>* %##reg##b2 x y z pointer##, align 32 %##nreg##b2 vx vy vz mass## = load <4 x double>, <4 x double>* %##reg##b2 vx vy vz mass pointer##, align 32 %##nreg##delta## = fsub <4 x double> %##reg##b1 x y z##, %##reg##b2 x y z## %##nreg##delta ^ 2## = fmul <4 x double> %##reg##delta##, %##reg##delta## ##llvmdeclare##llvm.vector.reduce.fadd.v4f64##declare double @llvm.vector.reduce.fadd.v4f64(double, <4 x double>)## %##nreg##distance ^ 2## = call double @llvm.vector.reduce.fadd.v4f64(double 0.0, <4 x double> %##reg##delta ^ 2##) ##llvmdeclare##llvm.sqrt.f64##declare double @llvm.sqrt.f64(double)## %##nreg##distance## = call double @llvm.sqrt.f64(double %##reg##distance ^ 2##) %##nreg##distance ^ 3## = fmul double %##reg##distance ^ 2##, %##reg##distance## %##nreg##mag## = fdiv double %##reg##dt##, %##reg##distance ^ 3## %##nreg##mag as vec## = insertelement <1 x double> undef, double %##reg##mag##, i32 0 %##nreg##mag vec## = shufflevector <1 x double> %##reg##mag as vec##, <1 x double> undef, <4 x i32> zeroinitializer %##nreg##b1 mass vec## = shufflevector <4 x double> %##reg##b1 vx vy vz mass##, <4 x double> undef, <4 x i32> <i32 3, i32 3, i32 3, i32 3> %##nreg##b1 mass * mag vec## = fmul <4 x double> %##reg##b1 mass vec##, %##reg##mag vec## %##nreg##b2 mass vec## = shufflevector <4 x double> %##reg##b2 vx vy vz mass##, <4 x double> undef, <4 x i32> <i32 3, i32 3, i32 3, i32 3> %##nreg##b2 mass * mag vec## = fmul <4 x double> %##reg##b2 mass vec##, %##reg##mag vec## %##nreg##-delta## = fneg <4 x double> %##reg##delta## ##llvmdeclare##llvm.fmuladd.v4f64##declare <4 x double> @llvm.fmuladd.v4f64(<4 x double>, <4 x double>, <4 x double>)## %##nreg##v1 - d * mass2 * mag## = call <4 x double> @llvm.fmuladd.v4f64(<4 x double> %##reg##b2 mass * mag vec##, <4 x double> %##reg##-delta##, <4 x double> %##reg##b1 vx vy vz mass##) %##nreg##v2 + d * mass1 * mag## = call <4 x double> @llvm.fmuladd.v4f64(<4 x double> %##reg##b1 mass * mag vec##, <4 x double> %##reg##delta##, <4 x double> %##reg##b2 vx vy vz mass##) store <4 x double> %##reg##v1 - d * mass2 * mag##, <4 x double>* %##reg##b1 vx vy vz mass pointer##, align 32 store <4 x double> %##reg##v2 + d * mass1 * mag##, <4 x double>* %##reg##b2 vx vy vz mass pointer##, align 32 %##nreg##counter 2 - 1## = sub i8 %##reg##counter 2##, 1 %##nreg##(counter 2 - 1) == 0## = icmp eq i8 %##reg##counter 2 - 1##, 0 %##nreg##next b2 pointer## = getelementptr <4 x double>, <4 x double>* %##reg##b2 vx vy vz mass pointer##, i64 1 br i1 %##reg##(counter 2 - 1) == 0##, label %##reg##end loop 2##, label %##reg##loop 2## ##nreg##end loop 2##: %##nreg##counter 1 - 1## = sub i8 %##reg##counter 1##, 1 %##nreg##(counter 1 - 1) == 0## = icmp eq i8 %##reg##counter 1 - 1##, 0 br i1 %##reg##(counter 1 - 1) == 0##, label %##reg##end loop 1##, label %##reg##loop 1## ##nreg##end loop 1##: %##nreg##tmp vec 1## = insertelement <4 x double> undef, double %##reg##dt##, i32 0 %##nreg##tmp vec 2## = insertelement <4 x double> %##reg##tmp vec 1##, double %##reg##dt##, i32 1 %##nreg##tmp vec 3## = insertelement <4 x double> %##reg##tmp vec 2##, double %##reg##dt##, i32 2 %##nreg##dt vec## = insertelement <4 x double> %##reg##tmp vec 3##, double 0.0, i32 3 br label %##reg##loop 3## ##nreg##loop 3##: %##nreg##b x y z pointer## = phi <4 x double>* [%##reg##bodies##, %##reg##end loop 1##], [%##reg##next b x y z pointer##, %##reg##loop 3##] %##nreg##counter 3## = phi i8 [5, %##reg##end loop 1##], [%##reg##counter 3 - 1##, %##reg##loop 3##] %##nreg##b vx vy vz mass pointer## = getelementptr <4 x double>, <4 x double>* %##reg##b x y z pointer##, i64 1 %##nreg##b x y z## = load <4 x double>, <4 x double>* %##reg##b x y z pointer##, align 32 %##nreg##b vx vy vz mass## = load <4 x double>, <4 x double>* %##reg##b vx vy vz mass pointer##, align 32 %##nreg##xyz + dt * v## = call <4 x double> @llvm.fmuladd.v4f64(<4 x double> %##reg##dt vec##, <4 x double> %##reg##b vx vy vz mass##, <4 x double> %##reg##b x y z##) store <4 x double> %##reg##xyz + dt * v##, <4 x double>* %##reg##b x y z pointer##, align 32 %##nreg##counter 3 - 1## = sub i8 %##reg##counter 3##, 1 %##nreg##(counter 3 - 1) == 0## = icmp eq i8 %##reg##counter 3 - 1##, 0 %##nreg##next b x y z pointer## = getelementptr <4 x double>, <4 x double>* %##reg##b vx vy vz mass pointer##, i64 1 br i1 %##reg##(counter 3 - 1) == 0##, label %##reg##end loop 3##, label %##reg##loop 3## ##nreg##end loop 3##: ret [2 x i64] zeroinitializer def main() const bodies Int = const::bodies.unsafe_offsetI64(3).toAlignedArray() const n Int = Int.fromString(getCMDLineArgument(1)) bodies.offsetMomentum() bodies.energy().println() for :(i Int = 0) i < n; i++ bodies.advance(0.01) bodies.energy().println() Все поставленные задачи были достигнуты, результатом доволен.Сcылки https://github.com/SharDeveloper/sharc https://github.com/SharDeveloper/shar-standart-module https://github.com/SharDeveloper/libshar-os-api_linux-x86_64 https://github.com/SharDeveloper/kate-shar-highlighting =========== Источник: habr.com =========== Похожие новости:
Программирование ), #_kompiljatory ( Компиляторы ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 03:14
Часовой пояс: UTC + 5