SlideShare a Scribd company logo
Introduction to
         Memoria
The Application Specific Data Structures Toolkit
Motivation I
●   Memory is cheap, but fast memory will always be
    expensive and limited in size.
●   Random access to DRAM memory is relatively
    slow and hasn't improved significantly for
    decades (it takes about 50 ns).
●   The sequential one is 10-100-1000 times faster.
●   Memory is hierarchical, the faster the memory,
    the less its size is.
●   And this limitation is physical (the speed of light
    is bounded).
●   We need to fit as much information into the
    limited memory as possible.
●   And we need data structures exploiting fast
    sequential access over the slow random one.
Motivation II
●   Most of data structures for main memory haven't changed for decades. They are
    still based on directly mapped linked lists where links are represented with memory
    pointers.
●   Pointer operations have O(1) theoretical complexity in the plain RAM model but in
    the hierarchical memory it is not true.
●   Moreover pointers consume too much memory, especially on 64-bit architectures.
    STL std::set<BigInt> consumes 40 bytes for every tree node (Linux GCC 4.6), this
    is 5 times larger than the size of data (sizeof(BigInt) = 8 bytes). An std::set<BigInt>
    containing 1M elements will consume 40 MiB of memory.
●   The situation is even worse for the in-memory representation of structured
    documents (XML, HTML, ODF etc) where the document representation consumes
    100+ times more memory than the raw data (check the Firefox memory usage).
●   Linked lists based data structures are simple and flexible, they just do their job. But
    how well does they perform on the modern hardware? Let's check performance of
    two different implementations of balanced search tree – one of the most
    fundamental data structures.
Balanced Search Trees
●   Let's consider balanced partial sums tree
    representing an ordered sequence of
    numbers (it is used in Memoria).
●   And instead of implementing it with well-
    known linked lists, lets pack it into an
    array: PackedTree and PackedSet.
●   We are not going to limit ourselves with
    binary tree case. PackedTree is multiary
    tree.
●   Let's check its performance under
    various conditions and compare with
    std::set<> (that is based on the binary
    RB-tree).
Introduction to Memoria
PackedTree Performance Analysis
●   PC Box for benchmarks has Intel Q9400 @ 3GHz with 2x3M L2 Cache CPU, 8G
    DDR2 RAM @ 750 MHz, Fedora Core 16 with GCC 4.6.3
●   When packed tree fits into the CPU cache, trees with low fanout perform better.
●   But when data is in memory, trees with high fanout perform better (except for the
    64-children one).
●   This is because balanced tree with high fanout has less levels that means less
    random access operations. And search for the next child in a node is sequential
    and quick.
●   DRAM has high latency and CPU loses hundreds of cycles in case of cache
    miss. These lost cycles are hidden reserve of performance for data structures.
●   Linked list based STL std::set<> is much faster than PackedTree if the data
    structure fits into CPU cache, but PackedTree is much faster otherwise.
●   This is despite the fact that packed tree performs much more mathematical
    operations to find one element of a sequence than pointer based RB-tree inside
    std::set<>.
Motivation III
●   The situation is even worse with external memory. Which is so slow at random
    access that each such external IO operation should be taken into account.
●   No size fits all here. Efficient data structures for external memory are always
    application specific or workload specific (think NoSQL).
●   It is true not only for external memory. Intelligent data processing requires such
    complex data structures as searchable sequences and bit vectors.
●   Giant volumes of hot structured data need succinct and compressed versions of
    data structures.
●   This is the reason why application specific data structures are black magic of
    computer science. Each of them incorporates significant amount of hardcore
    knowledge.
●   The only way to accelerate the progress in this area is solution sharing. To make
    this possible we need a development framework or toolkit for data structures in
    generalized hierarchical memory (from CPU registers to data grids).
What is Memoria?
●   Memoria for data structures is like Qt for GUI and like LLVM for program code. It
    separates logical data representation in the form of data structures from its physical
    representation in hierarchical memory. The only universal way to achieve high
    performance is to implicitly reorganize physical data layout according to workload-
    specific access patterns.
●   The word is Latin, and can be translated as "memory". Memoria was the term for
    aspects involving memory in Western classical rhetoric discourse.
●   Memoria is written in C++ with and relies heavily on template metaprogramming for
    data structure construction from basic building blocks. Memoria provides STL-like
    API for client code. GCC 4.6 and Clang 3.0 are supported.
●   It uses modular design with separation of concerns. Physical memory block
    management is isolated behind the Allocator interface.
●   Default implementation of Allocator (SmallInMemoryAllocator) provides serialization
    of the allocator's state to a stream and copy-on-write based transactions.
●   The project core provides templated balanced search tree as well as several basic
    data structures such as Map, Set, Vector and VectorMap over it.
The Hidden Reserve of
                  Performance
●   The fundamental data structure of Memoria is balanced
    tree of array-packed trees of limited size. Something
    like B-Tree but with many differences...
●   Balanced tree of Memoria is generalized by design and
    can be customized to various types of balanced search
    trees.
●   It is transactional and abstracted from physical memory
    manager (Allocator).
●   So huge amount of computational work is performed
    when balanced tree is read or written.
●   But, as benchmarks show, Memoria Set<> is only
    about 3 times slower than lightweight PackedSet, when
    the most part of the tree is in RAM.
●   And it is even faster than STL set<> in this case.
●   Even for linear ordered scan. Check it...
Introduction to Memoria
Introduction to Memoria
Memoria Update Performance
●   A cool feature of Memoria balanced search tree is batch
    update operations.
●   If multiple updates are performed in a batch then some
    amount of computational work can be shared between
    individual update operations.
●   There are two kinds of batching: batch
    insertions/deletions and transactional grouping.
●   There is no upper limit on batch or transaction size.
●   In our benchmarks random insertion rate went from about
    450K/sec for single insert to 20M/sec for 128-elements
    batch. And finally reached more than 80M/sec for
    batches of size 10K+ elements.
●   Placing several updates in one transaction does not
    introduce such giant performance improvement. Only 3-5
    times. Check it...
Introduction to Memoria
Introduction to Memoria
Update Rate Limits
●   How fast can updates be? Memoria uses 4K memory
    blocks for search tree nodes by default, variable block size
    is supported.
●   Let's Memove() data in randomly selected 4K blocks at
    randomly selected indexes (emulating insertions into
    blocks).
●   For arrays which don't fit in CPU cache we get about 2M
    moves/sec and about 4GB/sec of memory throughput (for
    our Q9400 PC Box).
●   So 2M insertions per second is an upper bound for random
    individual insertion rate of our balanced tree.
●   From the previous benchmark, 450K insertions/sec is not
    very far from this limit.
●   Memoria core data structure does not introduce significant
    performance overhead over hardware memory level.
●   And there is a room for improvements.
Introduction to Memoria
Dynamic Vector
●   Dynamic Vector is based on partial sums key-value
    map where key is an offset of the data block in the
    vector's index space and value is an ID of this
    block.
●   Partial sums tree provides insert/remove/access
    operations with O(Log N) worst-case complexity.
●   It is not a replacement of std::vector<> if fast
    random access is required.
●   But sequential read throughput is limited only with
    main memory. The benchmark shows that it comes
    quite close to the limit (4 GB/sec) even for 4K
    blocks.
●   Random access throughput for 4K blocks comes
    close to 1.5 GB/sec that is very high in absolute
    numbers :)
●   Random access read performance for 128 byte
    blocks is about 850K op/sec. Check it...
Introduction to Memoria
Vector Update Performance
●   Insertions are slower then reads mainly because it is
    necessary to move data in data blocks and update index
    tree for the new data.
●   In absolute numbers if sequential read comes close to
    4GB/s then sequential append reaches only 1.2 GB/sec for
    our PC Box.
●   Random insert memory throughput for 16K blocks comes
    close to 600MB/sec. That is much higher than current
    HDD/SSD are able to consume.
●   Random insert performance for 128 byte blocks is about
    550K writes/sec that is not far from 2M/sec practical limit.
●   Vector does not introduce significant overhead over
    hardware memory for random insertions.
●   Check it...
Introduction to Memoria
VectorMap
●   VectorMap is a mapping from BigInt (64
    bits) to dynamic vector region.
●   It is a combination of a dynamic vector and
    a set of pairs (Key, Offset) represented with
    two-key partial sum tree.
●   It is relatively succinct – only 8 or 16 bytes
    per entry (this value doesn't include internal
    search tree nodes). For 256 byte values
    total overhead is less than 10%.
●   300K random reads and 200K random
    writes of 128 byte values.
●   3.8/1.2 GB/sec memory read/wright
    throughput for 256K byte values.
●   Up to 2/0.3 GB/sec sequential read/write
    throughput for 256 byte values.
●   It also supports batch updates.
Introduction to Memoria
Introduction to Memoria
Introduction to Memoria
Roadmap
●   Better alignment with modern              ●   LOUDS/DFUDS succinct trees.
    theoretical results for balanced trees
    (cache-oblivious etc).
                                              ●   Generic searchable sequences of 1 to
                                                  8 bit symbols on top of
●   Multithreading with MVCC-like conflict        Vector/VectoMap.
    resolution.
                                              ●   Multiary Wavelet Tree and searchable
●   Native integration with various virtual       sequences over arbitrary alphabets.
    machines build on top of LLVM JIT.
                                              ●   Full-text search indexes for NLP and
●   Extending core support for external           other applications.
    memory.
                                              ●   Succinct graph representation.
●   Shared memory support for allocators
    to share data structures between
                                              ●   Etc...
    processes.                                ●   Etc...
●   Variable blocks size support.
●   Dynamic bit vector with rank()/select()
    operations.
Memoria
          Is still in active development
       But your contributions are welcome

If you are interested please follow us on Bitbucket
               http://bitbucket.org/vsmirnov/memoria

              For any questions feel free to contact me
                Victor Smirnov <aist11@gmail.com>

More Related Content

PDF
What should be done to IR algorithms to meet current, and possible future, ha...
PPT
Main MeMory Data Base
PPT
Cache
PPT
Memory management
PDF
Memory Management
PPTX
Project Presentation Final
PPTX
Associative memory 14208
What should be done to IR algorithms to meet current, and possible future, ha...
Main MeMory Data Base
Cache
Memory management
Memory Management
Project Presentation Final
Associative memory 14208

What's hot (20)

PPTX
Operating system paging and segmentation
PPTX
In-memory Databases
PPTX
Paging and Segmentation
PPT
Os Swapping, Paging, Segmentation and Virtual Memory
PPT
20. Parallel Databases in DBMS
PPT
Memory management ppt coa
PPTX
Computer architecture memory system
PPTX
In-Memory Computing: How, Why? and common Patterns
PPT
Ios103 ios102 iv-operating-system-memory-management_wk4
PPT
Csc4320 chapter 8 2
PDF
Main Memory
PPTX
Cache Memory Computer Architecture and organization
PPT
PPTX
Memory management
PPTX
memory Interleaving and low order interleaving and high interleaving
PPT
Memory management
PPT
Ch9 OS
 
PDF
call for papers, research paper publishing, where to publish research paper, ...
PPTX
Introduction of Memory Management
Operating system paging and segmentation
In-memory Databases
Paging and Segmentation
Os Swapping, Paging, Segmentation and Virtual Memory
20. Parallel Databases in DBMS
Memory management ppt coa
Computer architecture memory system
In-Memory Computing: How, Why? and common Patterns
Ios103 ios102 iv-operating-system-memory-management_wk4
Csc4320 chapter 8 2
Main Memory
Cache Memory Computer Architecture and organization
Memory management
memory Interleaving and low order interleaving and high interleaving
Memory management
Ch9 OS
 
call for papers, research paper publishing, where to publish research paper, ...
Introduction of Memory Management
Ad

Similar to Introduction to Memoria (20)

PPT
chapter6.ppt
PDF
Plam15 slides.potx
PPT
Lec10 Computer Architecture by Hsien-Hsin Sean Lee Georgia Tech -- Memory part2
DOC
Data structures project
PPT
Ct213 memory subsystem
DOCX
virtual memory
PDF
Code dive 2019 kamil witecki - should i care about cpu cache
PDF
Software Design for Persistent Memory Systems
PDF
Page Cache in Linux 2.6.pdf
PDF
Efficient Memory Management with Data Structures - Hiike
PPT
3620121datastructures.ppt
PDF
Everything We Learned About In-Memory Data Layout While Building VoltDB
PDF
Caching in (DevoxxUK 2013)
PPTX
A Developer's View Into Spark's Memory Model with Wenchen Fan
PPTX
A Developer’s View into Spark's Memory Model with Wenchen Fan
PPTX
Some key value stores using log-structure
ODP
Vmfs
PPT
Memory organization including cache and RAM.ppt
PDF
Buffer Trees - Utility and Applications for External Memory Data Processing
PDF
The life and times
chapter6.ppt
Plam15 slides.potx
Lec10 Computer Architecture by Hsien-Hsin Sean Lee Georgia Tech -- Memory part2
Data structures project
Ct213 memory subsystem
virtual memory
Code dive 2019 kamil witecki - should i care about cpu cache
Software Design for Persistent Memory Systems
Page Cache in Linux 2.6.pdf
Efficient Memory Management with Data Structures - Hiike
3620121datastructures.ppt
Everything We Learned About In-Memory Data Layout While Building VoltDB
Caching in (DevoxxUK 2013)
A Developer's View Into Spark's Memory Model with Wenchen Fan
A Developer’s View into Spark's Memory Model with Wenchen Fan
Some key value stores using log-structure
Vmfs
Memory organization including cache and RAM.ppt
Buffer Trees - Utility and Applications for External Memory Data Processing
The life and times
Ad

Recently uploaded (20)

PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PPTX
Programs and apps: productivity, graphics, security and other tools
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PPTX
MYSQL Presentation for SQL database connectivity
PDF
Network Security Unit 5.pdf for BCA BBA.
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Encapsulation_ Review paper, used for researhc scholars
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
Empathic Computing: Creating Shared Understanding
PDF
Encapsulation theory and applications.pdf
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Mobile App Security Testing_ A Comprehensive Guide.pdf
Programs and apps: productivity, graphics, security and other tools
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Reach Out and Touch Someone: Haptics and Empathic Computing
MYSQL Presentation for SQL database connectivity
Network Security Unit 5.pdf for BCA BBA.
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Per capita expenditure prediction using model stacking based on satellite ima...
Encapsulation_ Review paper, used for researhc scholars
Understanding_Digital_Forensics_Presentation.pptx
Digital-Transformation-Roadmap-for-Companies.pptx
Unlocking AI with Model Context Protocol (MCP)
Advanced methodologies resolving dimensionality complications for autism neur...
20250228 LYD VKU AI Blended-Learning.pptx
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Empathic Computing: Creating Shared Understanding
Encapsulation theory and applications.pdf
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...

Introduction to Memoria

  • 1. Introduction to Memoria The Application Specific Data Structures Toolkit
  • 2. Motivation I ● Memory is cheap, but fast memory will always be expensive and limited in size. ● Random access to DRAM memory is relatively slow and hasn't improved significantly for decades (it takes about 50 ns). ● The sequential one is 10-100-1000 times faster. ● Memory is hierarchical, the faster the memory, the less its size is. ● And this limitation is physical (the speed of light is bounded). ● We need to fit as much information into the limited memory as possible. ● And we need data structures exploiting fast sequential access over the slow random one.
  • 3. Motivation II ● Most of data structures for main memory haven't changed for decades. They are still based on directly mapped linked lists where links are represented with memory pointers. ● Pointer operations have O(1) theoretical complexity in the plain RAM model but in the hierarchical memory it is not true. ● Moreover pointers consume too much memory, especially on 64-bit architectures. STL std::set<BigInt> consumes 40 bytes for every tree node (Linux GCC 4.6), this is 5 times larger than the size of data (sizeof(BigInt) = 8 bytes). An std::set<BigInt> containing 1M elements will consume 40 MiB of memory. ● The situation is even worse for the in-memory representation of structured documents (XML, HTML, ODF etc) where the document representation consumes 100+ times more memory than the raw data (check the Firefox memory usage). ● Linked lists based data structures are simple and flexible, they just do their job. But how well does they perform on the modern hardware? Let's check performance of two different implementations of balanced search tree – one of the most fundamental data structures.
  • 4. Balanced Search Trees ● Let's consider balanced partial sums tree representing an ordered sequence of numbers (it is used in Memoria). ● And instead of implementing it with well- known linked lists, lets pack it into an array: PackedTree and PackedSet. ● We are not going to limit ourselves with binary tree case. PackedTree is multiary tree. ● Let's check its performance under various conditions and compare with std::set<> (that is based on the binary RB-tree).
  • 6. PackedTree Performance Analysis ● PC Box for benchmarks has Intel Q9400 @ 3GHz with 2x3M L2 Cache CPU, 8G DDR2 RAM @ 750 MHz, Fedora Core 16 with GCC 4.6.3 ● When packed tree fits into the CPU cache, trees with low fanout perform better. ● But when data is in memory, trees with high fanout perform better (except for the 64-children one). ● This is because balanced tree with high fanout has less levels that means less random access operations. And search for the next child in a node is sequential and quick. ● DRAM has high latency and CPU loses hundreds of cycles in case of cache miss. These lost cycles are hidden reserve of performance for data structures. ● Linked list based STL std::set<> is much faster than PackedTree if the data structure fits into CPU cache, but PackedTree is much faster otherwise. ● This is despite the fact that packed tree performs much more mathematical operations to find one element of a sequence than pointer based RB-tree inside std::set<>.
  • 7. Motivation III ● The situation is even worse with external memory. Which is so slow at random access that each such external IO operation should be taken into account. ● No size fits all here. Efficient data structures for external memory are always application specific or workload specific (think NoSQL). ● It is true not only for external memory. Intelligent data processing requires such complex data structures as searchable sequences and bit vectors. ● Giant volumes of hot structured data need succinct and compressed versions of data structures. ● This is the reason why application specific data structures are black magic of computer science. Each of them incorporates significant amount of hardcore knowledge. ● The only way to accelerate the progress in this area is solution sharing. To make this possible we need a development framework or toolkit for data structures in generalized hierarchical memory (from CPU registers to data grids).
  • 8. What is Memoria? ● Memoria for data structures is like Qt for GUI and like LLVM for program code. It separates logical data representation in the form of data structures from its physical representation in hierarchical memory. The only universal way to achieve high performance is to implicitly reorganize physical data layout according to workload- specific access patterns. ● The word is Latin, and can be translated as "memory". Memoria was the term for aspects involving memory in Western classical rhetoric discourse. ● Memoria is written in C++ with and relies heavily on template metaprogramming for data structure construction from basic building blocks. Memoria provides STL-like API for client code. GCC 4.6 and Clang 3.0 are supported. ● It uses modular design with separation of concerns. Physical memory block management is isolated behind the Allocator interface. ● Default implementation of Allocator (SmallInMemoryAllocator) provides serialization of the allocator's state to a stream and copy-on-write based transactions. ● The project core provides templated balanced search tree as well as several basic data structures such as Map, Set, Vector and VectorMap over it.
  • 9. The Hidden Reserve of Performance ● The fundamental data structure of Memoria is balanced tree of array-packed trees of limited size. Something like B-Tree but with many differences... ● Balanced tree of Memoria is generalized by design and can be customized to various types of balanced search trees. ● It is transactional and abstracted from physical memory manager (Allocator). ● So huge amount of computational work is performed when balanced tree is read or written. ● But, as benchmarks show, Memoria Set<> is only about 3 times slower than lightweight PackedSet, when the most part of the tree is in RAM. ● And it is even faster than STL set<> in this case. ● Even for linear ordered scan. Check it...
  • 12. Memoria Update Performance ● A cool feature of Memoria balanced search tree is batch update operations. ● If multiple updates are performed in a batch then some amount of computational work can be shared between individual update operations. ● There are two kinds of batching: batch insertions/deletions and transactional grouping. ● There is no upper limit on batch or transaction size. ● In our benchmarks random insertion rate went from about 450K/sec for single insert to 20M/sec for 128-elements batch. And finally reached more than 80M/sec for batches of size 10K+ elements. ● Placing several updates in one transaction does not introduce such giant performance improvement. Only 3-5 times. Check it...
  • 15. Update Rate Limits ● How fast can updates be? Memoria uses 4K memory blocks for search tree nodes by default, variable block size is supported. ● Let's Memove() data in randomly selected 4K blocks at randomly selected indexes (emulating insertions into blocks). ● For arrays which don't fit in CPU cache we get about 2M moves/sec and about 4GB/sec of memory throughput (for our Q9400 PC Box). ● So 2M insertions per second is an upper bound for random individual insertion rate of our balanced tree. ● From the previous benchmark, 450K insertions/sec is not very far from this limit. ● Memoria core data structure does not introduce significant performance overhead over hardware memory level. ● And there is a room for improvements.
  • 17. Dynamic Vector ● Dynamic Vector is based on partial sums key-value map where key is an offset of the data block in the vector's index space and value is an ID of this block. ● Partial sums tree provides insert/remove/access operations with O(Log N) worst-case complexity. ● It is not a replacement of std::vector<> if fast random access is required. ● But sequential read throughput is limited only with main memory. The benchmark shows that it comes quite close to the limit (4 GB/sec) even for 4K blocks. ● Random access throughput for 4K blocks comes close to 1.5 GB/sec that is very high in absolute numbers :) ● Random access read performance for 128 byte blocks is about 850K op/sec. Check it...
  • 19. Vector Update Performance ● Insertions are slower then reads mainly because it is necessary to move data in data blocks and update index tree for the new data. ● In absolute numbers if sequential read comes close to 4GB/s then sequential append reaches only 1.2 GB/sec for our PC Box. ● Random insert memory throughput for 16K blocks comes close to 600MB/sec. That is much higher than current HDD/SSD are able to consume. ● Random insert performance for 128 byte blocks is about 550K writes/sec that is not far from 2M/sec practical limit. ● Vector does not introduce significant overhead over hardware memory for random insertions. ● Check it...
  • 21. VectorMap ● VectorMap is a mapping from BigInt (64 bits) to dynamic vector region. ● It is a combination of a dynamic vector and a set of pairs (Key, Offset) represented with two-key partial sum tree. ● It is relatively succinct – only 8 or 16 bytes per entry (this value doesn't include internal search tree nodes). For 256 byte values total overhead is less than 10%. ● 300K random reads and 200K random writes of 128 byte values. ● 3.8/1.2 GB/sec memory read/wright throughput for 256K byte values. ● Up to 2/0.3 GB/sec sequential read/write throughput for 256 byte values. ● It also supports batch updates.
  • 25. Roadmap ● Better alignment with modern ● LOUDS/DFUDS succinct trees. theoretical results for balanced trees (cache-oblivious etc). ● Generic searchable sequences of 1 to 8 bit symbols on top of ● Multithreading with MVCC-like conflict Vector/VectoMap. resolution. ● Multiary Wavelet Tree and searchable ● Native integration with various virtual sequences over arbitrary alphabets. machines build on top of LLVM JIT. ● Full-text search indexes for NLP and ● Extending core support for external other applications. memory. ● Succinct graph representation. ● Shared memory support for allocators to share data structures between ● Etc... processes. ● Etc... ● Variable blocks size support. ● Dynamic bit vector with rank()/select() operations.
  • 26. Memoria Is still in active development But your contributions are welcome If you are interested please follow us on Bitbucket http://bitbucket.org/vsmirnov/memoria For any questions feel free to contact me Victor Smirnov <aist11@gmail.com>