Upload
vadim-shalts
View
1.771
Download
12
Embed Size (px)
Citation preview
Функциональные (персистентные) структуры данных иих влияние на архитектуру приложений
Vadim Shalts
Vadim Shalts Функциональные структуры данных и архитектура 1 / 86
Персистентные структуры данных и их анализ
Оговорки
Vadim Shalts Функциональные структуры данных и архитектура 2 / 86
Персистентные структуры данных и их анализ
Сase классы
Scala:
case class Foo(myval: String)
Java:final class Foo {
private final String myval;
public Foo(String initialValue) {this.myval = initialValue;
}
public String getValue() {return this.myval;
}
@Overridepublic boolean equals(Object o) {
// ....}
@Overridepublic int hashCode() {
// ...}
}
Возможные решения для Java
projectlombok.org
immutables.org
github.com/google/auto
www.jannocessor.org
Scala, Groovy, Clojure :)
Vadim Shalts Функциональные структуры данных и архитектура 3 / 86
Персистентные структуры данных и их анализ
Сase классы
Scala:
case class Foo(myval: String)
Java:final class Foo {
private final String myval;
public Foo(String initialValue) {this.myval = initialValue;
}
public String getValue() {return this.myval;
}
@Overridepublic boolean equals(Object o) {
// ....}
@Overridepublic int hashCode() {
// ...}
}
Возможные решения для Java
projectlombok.org
immutables.org
github.com/google/auto
www.jannocessor.org
Scala, Groovy, Clojure :)
Vadim Shalts Функциональные структуры данных и архитектура 3 / 86
Персистентные структуры данных и их анализ
Project valhalla. JEP 169: Value Objects
Vadim Shalts Функциональные структуры данных и архитектура 4 / 86
Персистентные структуры данных и их анализ
Важные свойства неизменяемых (immutable) объектов
Легко создавать, тестировать и использоватьЯвляются потокобезопаснымиХорошие кандидаты для кэшированияПоддерживают инвариант состоянияИсключения не могут “разломать” состояние
Vadim Shalts Функциональные структуры данных и архитектура 5 / 86
Персистентные структуры данных и их анализ
Класс будет неизменяемым, если верно следующее:
Класс объявляется как finalВсе его поля являются finalСсылка this не должна пропасть во время конструированияНе содержат методов, которые могли бы изменить наблюдаемоесостояние объектаЛюбые поля, содержащие ссылки на изменяемые объекты,например массивы, совокупности или изменяемые классы,например Date:
Являются приватнымиНикогда не возвращаются и никаким другим образом нестановятся доступными вызывающим операторамЯвляются единственными ссылками на те объекты, на которые ониссылаютсяНе изменяют после конструирования состояние объектов, накоторые они ссылаются
Vadim Shalts Функциональные структуры данных и архитектура 6 / 86
Персистентные структуры данных и их анализ
JLS. Семантика final полей
«A thread-safe immutable object is seen as immutable by all threads,even if a data race is used to pass references to the immutable objectbetween threads»
JLS 17.5
Vadim Shalts Функциональные структуры данных и архитектура 7 / 86
Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)
Data Processing in Exascale-class Computing Systems | April 27, 2011 | CRM 3
Transistors (thousands) Single-thread Performance (SpecINT) Frequency (MHz) Typical Power (Watts) Number of Cores
Original data collected and plotted by M. Horowitz, F. Labonte, O. Shacham, K. Olukotun, L. Hammond and C. Batten Dotted line extrapolations by C. Moore
35 YEARS OF MICROPROCESSOR TREND DATA
Vadim Shalts Функциональные структуры данных и архитектура 8 / 86
Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)
Закон Амдала. Рост производительностивычислительной системы
Vadim Shalts Функциональные структуры данных и архитектура 9 / 86
Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)
Функциональное программирование
«In computer science, functional programming is aprogramming paradigm, a style of building the structure andelements of computer programs, that treats computation asthe evaluation of mathematical functions and avoids stateand mutable data.»
wikipedia
Vadim Shalts Функциональные структуры данных и архитектура 10 / 86
Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)
Простая нотация
f (x)Что скрывается за простой нотацией?
Vadim Shalts Функциональные структуры данных и архитектура 11 / 86
Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)
Сложность f(x)
int[] func(int[] param);
Какова сложность, если параметр:простой типпростой объектколлекция простых типов...
Vadim Shalts Функциональные структуры данных и архитектура 12 / 86
Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)
Традиционный подход
class Order {private ArrayList<Item> items = EMPTY_LIST;private double total = 0.0;
void addItem(Item item) {items.add(item);total = calcTotal();
}
String createCheck() {// use items and total ...// ....
}
// ....}
Vadim Shalts Функциональные структуры данных и архитектура 13 / 86
Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)
Традиционный подход
class Order {private ArrayList<Item> items = EMPTY_LIST;private double total = 0.0;
synchronized void addItem(Item item) {items.add(item);total = calcTotal();
}
synchronized String createCheck() {// use items and total ...// ....
}
// ....}
Vadim Shalts Функциональные структуры данных и архитектура 14 / 86
Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)
Java Language and Virtual Machine Specifications (14.19)
If execution of the Block completes normally, then themonitor is unlocked and the synchronized statementcompletes normally.
If execution of the Block completes abruptly for anyreason, then the monitor is unlocked and thesynchronized statement completes abruptly for thesame reason.
Vadim Shalts Функциональные структуры данных и архитектура 15 / 86
Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)
Потеряна предсказуемость
Потеряна предсказуемость.Что можно сделать?
Vadim Shalts Функциональные структуры данных и архитектура 16 / 86
Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)
Традиционный подход. Сохраняем состояние ошибки
class Order {private ArrayList<Item> items = EMPTY_LIST;private double total = 0.0;private boolean incorrectState = false;
synchronized void addItem(Item item) {
if (incorrectState)throw new IllegalStateException();
incorrectState = true;
items.add(item);total = calcTotal();
incorrectState = false;}
// ....}
Vadim Shalts Функциональные структуры данных и архитектура 17 / 86
Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)
Традиционный подход. Предотвращаем потерюсостояния
class Order {private List<Item> items = EMPTY_LIST;private double total = 0.0;
synchronized void addItem(Item item) {List<Item> newItems =
fastCopyAndAppend(items, item); // Как?
double newTotal = calcTotal(newItems);
items = newItems;total = newTotal;
}
// ....}
Vadim Shalts Функциональные структуры данных и архитектура 18 / 86
Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)
Еще лучше сделать класс Order неизменяемым(immutable)!
«Classes should be immutable unless there’s a very goodreason to make them mutable.»
Joshua Bloch
«If an object cannot change state, then it can neverencounter conflicts or inconsistencies when multiple activitiesattempt to change its state in incompatible ways. Programsare much simpler to understand if existing objects are neverchanged, but instead new ones are continually created duringthe course of any computation.»
Doug Lea
Vadim Shalts Функциональные структуры данных и архитектура 19 / 86
Персистентные структуры данных и их анализ Определимся с терминологией
Термины
Неизменяемые (Immutable) структуры данных послесоздания не могут быть изменены
Immutable структуры данных (обычно) могут быть скопированы сизменениями для создания новой версииСоздание новой версии требует столько же памяти сколькопотребляет оригинал.
Персистентные (Persistent) структуры данных послеизменения содержат старую и новую версию данных
Persistent структуры данных являются Immutable в том смысле, чтосоздание новой версии не приводит к изменению старых версийСоздание новой версии может потребовать копирования данных изоригинала. При этом старая и новые версии будут содержатьобщие данные
Vadim Shalts Функциональные структуры данных и архитектура 20 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Обозначения
val x = X Y Z null
val x = X Y Z
Vadim Shalts Функциональные структуры данных и архитектура 21 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Список. Начальный
val x1 = X Y Z
Vadim Shalts Функциональные структуры данных и архитектура 22 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Список. Добавление
val x1 = X Y Z
val x2 = W
Vadim Shalts Функциональные структуры данных и архитектура 23 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Список. Удаление
val x1 = X Y Z
val x2 = W
val x3 =
Vadim Shalts Функциональные структуры данных и архитектура 24 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Как получить такие коллекции в Java?
<dependency><groupId>org.clojure</groupId><artifactId>clojure</artifactId><version>1.6.0</version>
</dependency>
Илиgithub.com/krukow/clj-ds
pcollections.orgwww.functionaljava.org
Vadim Shalts Функциональные структуры данных и архитектура 25 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Использование списка из Java
import com.github.krukow.clj_ds.*;import java.util.LinkedList;
public class ClojureListTest {
public static void main(String[] args) {PersistentList<String>
listCopy = Persistents.linkedList(new LinkedList());
PersistentList<String> list =Persistents.linkedList("my", "list");
PersistentList<String> extended = list.plus("!!!");PersistentList<String> cutted = extended.minus().minus();
System.out.println(list); // ("my" "list")System.out.println(extended); // ("!!!" "my" "list")System.out.println(cutted); // ("list")
}}
Vadim Shalts Функциональные структуры данных и архитектура 26 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
FIFO очередь. Начальное состояние
val x1 =
Queue
enqueue list
dequeue list
5 4
1 2 3
Vadim Shalts Функциональные структуры данных и архитектура 27 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
FIFO очередь. Вставка
val x1 =
Queue
enqueue list
dequeue list
5 4
1 2 3
Queue
enqueue list
dequeue list
val x2 =6
Vadim Shalts Функциональные структуры данных и архитектура 28 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
FIFO очередь. Удаление
val x1 =
Queue
enqueue list
dequeue list
5 4
1 2 3
Queue
enqueue list
dequeue list
val x2 =6
Queue
enqueue list
dequeue list
val x3 =
Vadim Shalts Функциональные структуры данных и архитектура 29 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
FIFO очередь. Удаление
val x5 =
Queue
enqueue list
dequeue list
Queue
enqueue list
dequeue list
Queue
enqueue list
dequeue list
val x6 =
6 5 4
4 5 6
Vadim Shalts Функциональные структуры данных и архитектура 30 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Использование очереди из Java
import com.github.krukow.clj_lang.PersistentQueue;
public class ClojureQueueTest {
public static void main(String[] args) {PersistentQueue empty = PersistentQueue.EMPTY;
PersistentQueue queue = empty.cons("my").cons("queue");PersistentQueue extended = queue.cons("!!!");PersistentQueue cutted = extended.pop();
System.out.println(queue.seq()); // ("my" "queue")System.out.println(extended.seq()); // ("my" "queue" "!!!")System.out.println(cutted.seq()); // ("queue" "!!!")
}}
Vadim Shalts Функциональные структуры данных и архитектура 31 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Вектор (Vector)Bit-partitioned hash tries
... 00000 00000 00000 = 0
... 00000 00000 11111 = 31
... 00000 00001 00000 = 32
... 00000 00001 11111 = 63
Values
bits 0-4
bits 5-9
Root
0 1 31 32 33 63
0 1 ... ... 31 0 1 ... ... 31
0 1
v1
Vadim Shalts Функциональные структуры данных и архитектура 32 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Вектор (Vector). Замена элемента
Values
bits 0-4
bits 5-9
Root
0 1 31 32 33 63 Z
0 1 ... ... 31 0 1 ... ... 31 0 1 ... ... 31
0 1 0 1
v1 v2
Vadim Shalts Функциональные структуры данных и архитектура 33 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Вектор (Vector). Полная структура
Values
bits 0-4
bits 5-9
Root
0 1 31 32 33 63 64 65
0 1 ... ... 31 0 1 ... ... 31
0 1
0 1
v1
Vadim Shalts Функциональные структуры данных и архитектура 34 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Вектор (Vector). Добавление
Values
bits 0-4
bits 5-9
Root
0 1 31 32 33 63 64 65 66
0 1 ... ... 31 0 1 ... ... 31
0 1 0 1 2
0 1
v1 v2
Vadim Shalts Функциональные структуры данных и архитектура 35 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Vector. Пример
import com.github.krukow.clj_ds.*;
public class PersistentVectorTest {
public static void main(String[] args) {PersistentVector<Integer> p = Persistents.vector(1, 2, 3);
PersistentVector<Integer> extended = p.plus(4).plus(5);PersistentVector<Integer> updated = p.plusN(1, 22);PersistentVector<Integer> cutted = p.minus();
System.out.println(p); // [1 2 3]System.out.println(extended); // [1 2 3 4 5]System.out.println(updated); // [1 22 3]System.out.println(cutted); // [1 2]
}}
Vadim Shalts Функциональные структуры данных и архитектура 36 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Улучшаем скорость. Переходные (Transient) структурыданных
Инсоляция в рамках одного потокаO(1)
создание из персистентных структур данныхОбщие данных с персистентным источникомO(1)
создание персистентных структур данных, когдаредактирование завершеноНельзя использовать после создания конечной персистентнойверсииБыстрые
Vadim Shalts Функциональные структуры данных и архитектура 37 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Transient структуры данных. Пример
import com.github.krukow.clj_ds.*;
public class TransienVectorTest {
public static void main(String[] args) {PersistentVector p = Persistents.vector(1, 2, 3);
TransientVector t = p.asTransient();
TransientVector tf = t.plus(4).plus(5); // efficient bulk modification
PersistentVector p2 = tf.persist();
System.out.println(p); // [1 2 3]System.out.println(p2); // [1 2 3 4 5]
}}
Vadim Shalts Функциональные структуры данных и архитектура 38 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
До создания Transient
Values
bits 0-4
bits 5-9
Root
0 1 31 32 33 63 64 65
0 1 ... ... 31 0 1 ... ... 31
0 1
0 1
v1
Vadim Shalts Функциональные структуры данных и архитектура 39 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Transient Vector
Values
bits 0-4
bits 5-9
Root
0 1 31 32 33 63 64 65
0 1 ... ... 31 0 1 ... ... 31
0 1
0 1
v1 Transient
Vadim Shalts Функциональные структуры данных и архитектура 40 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Transient Vector. Дешёвое добавление
Values
bits 0-4
bits 5-9
Root
0 1 31 32 33 63 64 65 66
0 1 ... ... 31 0 1 ... ... 31
0 1 0 1 ... ... 31
0 1
v1 Transient
Vadim Shalts Функциональные структуры данных и архитектура 41 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Transient Vector. Дешёвое добавление
Values
bits 0-4
bits 5-9
Root
0 1 31 32 33 63 64 65 66 67
0 1 ... ... 31 0 1 ... ... 31
0 1 0 1 ... ... 31
0 1
v1 Transient
Vadim Shalts Функциональные структуры данных и архитектура 42 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Возврат к Persistent Vector
Values
bits 0-4
bits 5-9
Root
0 1 31 32 33 63 64 65 66 67
0 1 ... ... 31 0 1 ... ... 31
0 1 0 1 2 3
0 1
v1 v2
Vadim Shalts Функциональные структуры данных и архитектура 43 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
RRB trees - Relaxed Radix Balanced Trees
“RRB-Trees: Efficient Immutable Vectors”,EPFL-REPORT-169879, September, 2011:http://infoscience.epfl.ch/record/169879/files/RMTrees.pdf
Дополнительные операции c O(logN
):
объединение коллекций (concatenation)вставка в позицию (insert-at)разделение на части (splits-at)
Реализации:github.com/clojure/core.rrb-vectorgithub.com/TiarkRompf/rrbtrees
Vadim Shalts Функциональные структуры данных и архитектура 44 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
HashMap. Hash Array Mapped Trie (HAMT)
Bit-partitioned hash tries
Vadim Shalts Функциональные структуры данных и архитектура 45 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
HashMap. ДобавлениеPath Copyingint count 15
INode root
HashMapint count 16
INode root
HashMap
Vadim Shalts Функциональные структуры данных и архитектура 46 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Общие свойства
ВерсионностьПотоковая безопасностьБезопасность для итерированияУменьшение потребление памяти (для несколькихкопий)Возможно увеличение потребления памяти (дляодной копии)Простота использованияПроизводительность?
Vadim Shalts Функциональные структуры данных и архитектура 47 / 86
Персистентные структуры данных и их анализ Примеры структур данных и их анализ
Теория за бортом
Детальный анализ производительностиЛинзы (Functional lenses) - упрощают работу свложенными неизменяемыми структурами данныхZipper - техника представления объединённыхструктур данных. Ускоряет обработку персистентныхколлекций.Ctrie - concurrent hash-trie
Vadim Shalts Функциональные структуры данных и архитектура 48 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Архитектура
Vadim Shalts Функциональные структуры данных и архитектура 49 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Undo/Redo. Команды
trait UndoableCommand {def redo()def undo()
}
var currentState = new StringBuilder()
class InsertCommand(pos: Int, value: String)extends UndoableCommand {
def redo() = currentState.insert(pos, value)def undo() = currentState.delete(pos, value.length)
}
Vadim Shalts Функциональные структуры данных и архитектура 50 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Undo/Redo + шаблон Хранитель (Memento)
case class Memento(storedState: String)
var currentState = new StringBuilder()val stateHistory = collection.mutable.Stack[Memento]()
class InsertCommand(pos: Int, value: String)extends UndoableCommand {
def redo() = {stateHistory.push( new Memento(currentState.mkString) )currentState.insert(pos, value)
}def undo() = {
currentState.clear()currentState.append( stateHistory.pop.storedState )
}}
Vadim Shalts Функциональные структуры данных и архитектура 51 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Undo/Redo + Персистентные структуры данных
case class Article(name: String, price: Double)case class Order(goods: Vector[Article], total: Double)
type State = Ordertype UndoableCommand = State => State
var currentState = new State(Vector.empty, 0)val stateHistory = collection.mutable.Stack[State]()
def executeCommand(command: UndoableCommand) = {stateHistory.push( currentState )currentState = command(currentState)
}
def undoCommand() =currentState = stateHistory.pop()
Vadim Shalts Функциональные структуры данных и архитектура 52 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Undo/Redo + Персистентные структуры данных
def buyArticle(article: Article): UndoableCommand =(state: State) =>
new State(state.goods :+ article,state.total + article.price)
// someCommand is function: State => Stateval someCommand = buyArticle(new Article("iPad", 499))
executeCommand(someCommand)println(state.total) // 499.0
undoCommand()println(state.total) // 0.0
Vadim Shalts Функциональные структуры данных и архитектура 53 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Блокировки. synchronized и ReentrantLock
Now t
Thread 1
Thread 2
Thread 3
Thread 4
Vadim Shalts Функциональные структуры данных и архитектура 54 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Блокировки. ReentrantReadWriteLock
Now t
Thread 1
Thread 2
Thread 3
Thread 4
Vadim Shalts Функциональные структуры данных и архитектура 55 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Блокировки. ReentrantReadWriteLock другой пример
Now t
Thread 1
Thread 2
Thread 3
Thread 4
Vadim Shalts Функциональные структуры данных и архитектура 56 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Блокировки. StampedLock
Now t
Thread 1
Thread 2
Thread 3Thread 3
Thread 4
Vadim Shalts Функциональные структуры данных и архитектура 57 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
StampedLock. Лучше масштабируется
class Point {private double x, y;private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) { // an exclusively locked methodlong stamp = sl.writeLock();try {
x += deltaX;y += deltaY;
} finally {sl.unlockWrite(stamp);
}}
// ...}
Vadim Shalts Функциональные структуры данных и архитектура 58 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
StampedLock. Оптимистичные блокировки
class Point {private double x, y;private final StampedLock sl = new StampedLock();
double distanceFromOrigin() { // A read-only methodlong stamp = sl.tryOptimisticRead();double currentX = x, currentY = y;if (!sl.validate(stamp)) {
stamp = sl.readLock();try {
currentX = x;currentY = y;
} finally {sl.unlockRead(stamp);
}}return Math.sqrt(currentX * currentX + currentY * currentY);
}
// ...}
Vadim Shalts Функциональные структуры данных и архитектура 59 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Оптимистичные блокировки через StampedLock.Проблемы и ограничения
«The use of optimistic mode for short read-only codesegments often reduces contention and improves throughput.However, its use is inherently fragile. Optimistic read sectionsshould only read fields and hold them in local variables forlater use after validation. Fields read while in optimisticmode may be wildly inconsistent, so usage applies only whenyou are familiar enough with data representations to checkconsistency and/or repeatedly invoke method validate()»
StampedLock. Java Documentation
Vadim Shalts Функциональные структуры данных и архитектура 60 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Блокировки. StampedLock
Now t
Thread 1
Thread 2
Thread 3
Thread 4
Vadim Shalts Функциональные структуры данных и архитектура 61 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Блокировки. Неизменяемые данные
t
Thread 1Thread 1
Thread 2Thread 2Thread 2
Thread 3Thread 3
Thread 4Thread 4
Vadim Shalts Функциональные структуры данных и архитектура 62 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Блокировки. Много писателей
@volatile var currentState: State = initialStateval writersLock = new StampedLock
def readState() = {val latestState = currentState// ...
}
def updateState(command: Command) = {val stamp = writersLock.writeLockInterruptibly()val latestState = currentStatetry {
currentState = command(currentState)} catch {
case AbortException => // ...case ex: Throwable => // ...
} finally {writersLock.unlockWrite(stamp)
}}
Vadim Shalts Функциональные структуры данных и архитектура 63 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Реализация с неизменяемым Point классом.Общий код
trait ReferenceManager[T] {def setState(state: T) = updateState(_ => state)def getState(): Tdef updateState(stateReplacer: T => T)
}
Vadim Shalts Функциональные структуры данных и архитектура 64 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Реализация с неизменяемым Point классом.Общий код
class LockBasedReferenceManager[T] extends ReferenceManager[T] {
@volatile private var currentState: T = _private val writersLock = new StampedLock
def getState() = currentState
def updateState(stateReplacer: T => T) = {val stamp = writersLock.writeLockInterruptibly()val latestState = currentStatetry {
currentState = stateReplacer(latestState)} catch {
case ex: Throwable => // ...} finally {
writersLock.unlockWrite(stamp)}
}}
Vadim Shalts Функциональные структуры данных и архитектура 65 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Реализация с неизменяемым Point классом.Основная логика
case class Point(x: Double, y: Double) {def distanceFromOrigin() = Math.sqrt(x * x + y * y)def move(deltaX: Double, deltaY: Double) =
new Point(x + deltaX, y + deltaY)}
class PointHolder(reference: ReferenceManager[Point]) {reference.setState(Point(0, 0)) // Initial state
def distanceFromOrigin() =reference.getState().distanceFromOrigin()
def move(deltaX: Double, deltaY: Double) =reference.updateState { s => s.move(deltaX, deltaY) }
}
Vadim Shalts Функциональные структуры данных и архитектура 66 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Блокировки. Оптимистичное обновление состояния
t
Thread 1Thread 1Thread 1Thread 1
Thread 2
Thread 3Thread 3
Thread 4Thread 4
Vadim Shalts Функциональные структуры данных и архитектура 67 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Реализация через CAS. Сходство с MVCC
class OptimisticReferenceManager[T] extends ReferenceManager[T] {
val ref = new AtomicReference[T]
override def setState(state: T) = ref.set(state)
def getState() = ref.get
def updateState(stateReplacer: T => T) = {var continue = true
while(continue) {val latestState = getState()val newState = stateReplacer(latestState)
if (ref.compareAndSet(latestState, newState))continue = false
}}
}
Vadim Shalts Функциональные структуры данных и архитектура 68 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
STM - Software transactional memory
Механизм управления параллелизмом аналогичныймеханизму транзакций баз данных.
Используется для управления доступом к совместноиспользуемой памяти в параллельных вычислениях.
Альтернатива для синхронизации на основеблокировок.
Vadim Shalts Функциональные структуры данных и архитектура 69 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Clojure STM
Реализация основана на MVCCТранзакции обеспечиваются изоляцией снимков(Snapshot isolation)Оптимистичный подход, рассчитываем на малуювероятность коллизий на записьНевозможны deadlocks, livelocks или гонкиКомпонуемые операции
Vadim Shalts Функциональные структуры данных и архитектура 70 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
STM - полезен
Если паттерны доступа укладываются в:частые чтениядостаточно редкие конфликты на запись
Почему?Если транзакция не удалась, то нужно выполнитьоткатНужно минимизировать пенальти за откат имаксимизировать параллелизм.
Vadim Shalts Функциональные структуры данных и архитектура 71 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
STM - Механика
Значения вычитанные внутри транзакции:гарантированно целостные и отражают всеизмененияотносятся к транзакциям, которые были завершеныдо начала этой транзакции.
Изменения внутри транзакция:локальны и видимы только внутри транзакциистановятся целиком видимыми после коммита
Vadim Shalts Функциональные структуры данных и архитектура 72 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
STM. Работа с ссылками
Слайд Neale Swinnerton (Clojure STM - What? Why? How?)Vadim Shalts Функциональные структуры данных и архитектура 73 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
И снова менеджер
def as[T](value: AnyRef) = value.asInstanceOf[T]
class StmReferenceManager[T <: AnyRef]extends ReferenceManager[T] {
private val ref = new Ref(null)
override def setState(state: T) = as[T]( ref.set(state) )
def getState() = as[T]( ref.deref() )
def updateState(stateReplacer: T => T) = ref.alter(new AFn() {override def invoke(state: AnyRef): AnyRef = as[T]{
as[AnyRef]( stateReplacer( as[T](state) ) )}
}, null)}
Vadim Shalts Функциональные структуры данных и архитектура 74 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Скрываем работу с транзакциями
def atomic[T](func: => T) = as[T] {LockingTransaction.runInTransaction(new Callable[T] {
def call = func})
}
Vadim Shalts Функциональные структуры данных и архитектура 75 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Надёжный, дерзкий, губернский банк
case class History(change: Double, balanceBeforeChange: Double)
case class Account(balance: Double, history: Vector[History]) {
if (balance < 0 || history.length > 100)throw new IllegalArgumentException("Not today")
if (history.length > 0 && history.last.change < 0.10)throw new IllegalArgumentException("Suspicious behavior")
def changeBalanceBy(amount: Double) =new Account(balance + amount, history :+ History(amount, balance))
}
class AccountHolder(reference: ReferenceManager[Account]) {reference.setState(Account(0, Vector())) // Initial state
def balance = reference.getState().balance
def changeBalanceBy(amount: Double) =reference.updateState ( s => s.changeBalanceBy(amount) )
}
Vadim Shalts Функциональные структуры данных и архитектура 76 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Соединяем всё вместе
val (source, target) = atomic {(new AccountHolder(new StmReferenceManager()),new AccountHolder(new StmReferenceManager()))
}
try {atomic {
target.changeBalanceBy(200)println(target.balance) // 200.0source.changeBalanceBy(-200) // IllegalStateException: Balance negative
}} catch {
case ex: Throwable => ex.printStackTrace();}
println(source.balance) // 0.0println(target.balance) // 0.0
source.changeBalanceBy(200) // IllegalStateException: No transaction running
Vadim Shalts Функциональные структуры данных и архитектура 77 / 86
Влияние на архитектуру приложений Применение и примеры архитектуры
Транзакции. ACI(D) для персистентных структур
✓ Atomicity (Атомарность)✓ Consistency (Согласованность)✓ Isolation (Изолированность)✗ Durability (Долговечность)
Vadim Shalts Функциональные структуры данных и архитектура 78 / 86
Влияние на архитектуру приложений Персистентные базы данных (Datomic)
Базы данных. Классика
Vadim Shalts Функциональные структуры данных и архитектура 79 / 86
Влияние на архитектуру приложений Персистентные базы данных (Datomic)
Datomic. Общая идея
Vadim Shalts Функциональные структуры данных и архитектура 80 / 86
Влияние на архитектуру приложений Персистентные базы данных (Datomic)
Datomic
Иммутабильная база данных с поддержкой ACID
Явным образом оперирует временем - время всегда частьюмодели данных
Основная единица хранения данных - факт привязанный ковремени (datom)
Datom - запись со списком атрибутов.
Атрибуты могут ссылаться на другие факты - отношения
Изменения модели происходит путём добавления новыхфактов
Vadim Shalts Функциональные структуры данных и архитектура 81 / 86
Влияние на архитектуру приложений Персистентные базы данных (Datomic)
Datomic 2
Полагается на внешнюю реализацию хранилища данных(RAM, DynamoDB, RDBMS, ...)
Изменения/транзакции отделены от чтения и проходят черезспециальный компонент (transactor).
Возможно симулирование состояния базы без коммитов
Практически безграничное масштабирование запросов начтение и встроенная поддержка кеширования
Запросы могут использовать функции на Java/Clojure.
Vadim Shalts Функциональные структуры данных и архитектура 82 / 86
Влияние на архитектуру приложений Персистентные базы данных (Datomic)
Datomic - возможные проблемы.
Платная дорогая лицензия (есть бесплатная урезаннаяверсия и версия для разработчиков)
Плохо подходит для хранения больших (BLOB) или частоменяющихся данных.
Значительно увеличивается потребность в дисковомпространстве (особенно индексы)
Для узлов сети желательно использовать большой кеш
Законы могут требовать возможности удалять данныепользователя.
Vadim Shalts Функциональные структуры данных и архитектура 83 / 86
Влияние на архитектуру приложений Персистентные базы данных (Datomic)
Персистентные структуры данных - итоги
Выгодный компромисс - платим памятью замасштабируемость
Изменения общих данных не блокируют обработку текущихзапросов (настройки, текущее состояние и т.д)
Долгие чтения не блокируют записи (обращение по сети,построение отчета)
Упрощается обработка долгих запросов (не связаныограничениями по времени)
Vadim Shalts Функциональные структуры данных и архитектура 84 / 86
Влияние на архитектуру приложений Персистентные базы данных (Datomic)
Персистентные структуры данных - итоги 2
Стратегия работы с блокировками стала гибкой и не связанажестко с реализацией логики
Упрощается тестирование и разработка бизнес логики(наиболее часто меняющийся код)
Повышается надёжность - случайные ошибки (исключения)имеют меньше шансов навредить
Доступность старых фактов/данных упрощает разработку иоткрывает новые возможности (datomic + blueprints)
Vadim Shalts Функциональные структуры данных и архитектура 85 / 86
Вопросы
Вопросы?
Vadim Shalts Функциональные структуры данных и архитектура 86 / 86