From 28e115b04f5522a0f63b0e904792f62b5ba55086 Mon Sep 17 00:00:00 2001 From: enricsosa Date: Wed, 14 Feb 2024 10:38:39 +0100 Subject: [PATCH] First docu's commit --- .gitignore | 1 + .readthedocs.yaml | 23 ++ docs/source/applications.rst | 91 ++++++ docs/source/basics.rst | 391 +++++++++++++++++++++++ docs/source/conf.py | 27 ++ docs/source/conf_param.rst | 51 +++ docs/source/cplusplus.rst | 293 +++++++++++++++++ docs/source/deployment.rst | 23 ++ docs/source/exe_pycompss.rst | 21 ++ docs/source/execution.rst | 38 +++ docs/source/images/DataLocality_4.png | Bin 0 -> 47399 bytes docs/source/images/HecubaBSCLogo2_1.png | Bin 0 -> 15435 bytes docs/source/images/HecubaIterators_2.png | Bin 0 -> 27644 bytes docs/source/images/SNSplit_3.png | Bin 0 -> 10714 bytes docs/source/index.rst | 33 ++ docs/source/lambda.rst | 64 ++++ docs/source/pycompss.rst | 30 ++ requirements.txt | 3 + 18 files changed, 1089 insertions(+) create mode 100644 .readthedocs.yaml create mode 100644 docs/source/applications.rst create mode 100644 docs/source/basics.rst create mode 100644 docs/source/conf.py create mode 100644 docs/source/conf_param.rst create mode 100644 docs/source/cplusplus.rst create mode 100644 docs/source/deployment.rst create mode 100644 docs/source/exe_pycompss.rst create mode 100644 docs/source/execution.rst create mode 100644 docs/source/images/DataLocality_4.png create mode 100644 docs/source/images/HecubaBSCLogo2_1.png create mode 100644 docs/source/images/HecubaIterators_2.png create mode 100644 docs/source/images/SNSplit_3.png create mode 100644 docs/source/index.rst create mode 100644 docs/source/lambda.rst create mode 100644 docs/source/pycompss.rst diff --git a/.gitignore b/.gitignore index c21fb737..af55d9d9 100644 --- a/.gitignore +++ b/.gitignore @@ -144,6 +144,7 @@ CMakeFiles CMakeScripts Testing Makefile +make.bat cmake_install.cmake install_manifest.txt compile_commands.json diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..c35581a7 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,23 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/source/conf.py + +# We recommend specifying your dependencies to enable reproducible builds: +# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +python: + install: + - requirements: requirements.txt + diff --git a/docs/source/applications.rst b/docs/source/applications.rst new file mode 100644 index 00000000..9d66597a --- /dev/null +++ b/docs/source/applications.rst @@ -0,0 +1,91 @@ +.. _applications: + +Hecuba applications at a glance +=============================== + +One of the goals of Hecuba is to provide programmers with an easy and portable interface to access data. This interface is independent of the type of system and storage used to keep data, enhancing the portability of the applications. To sum up, using Hecuba the applications can access data like regular objects stored in memory and Hecuba translates the code at runtime into the proper code, according to the backing storage used in each scenario. + +The current implementation of Hecuba handles in-memory objects, or persistent data storage provided by Apache Casssandra databases. This chapter guides how to create a Python application that uses Hecuba object abstractions to handle data persistence. + +We will start by defining a set of classes that represent the persistent data. The user must inherit from one of the main abstractions provided by Hecuba, thus, a *StorageObj* or a *StorageDict*. Programmers can also use *StorageNumpy* Hecuba class to instantiate persistent numpy ndarrays. + +The StorabeObj allows the user to define persistent attributes, accessed with the python object protocol. On the other hand, the StorageDict behaves like a python dictionary, accepting a key to identify the values. In both cases, the in-memory and persistent data will be handled transparently. + +Next, the user must define the data model with the concrete data types that will be stored in a persistent layer. The specification is written as a Python comment, and the structure differs if we inherit from a *StorageObj* or a *StorageDict*. For instance, to define a set of attributes we will use the ``@ClassField`` with a *StorageObj*, and to define a dictionary the ``@TypeSpec`` with a *StorageDict*. + +.. code-block:: python + + from hecuba import StorageObj + import numpy as np + + class Dataset(StorageObj): + ''' + @ClassField author str + @ClassField open_access bool + @ClassField injected_particles int + @ClassField geometry numpy.ndarray + ''' + +The user data model expresses that there will be four attributes which must be stored onto the persistent layer, for each instance of the class. Also, by adding different ``@ClassField`` the user can define any number of persistent attributes. Once the class is defined, the application can instantiate as many objects as needed. On the other hand, we can have a *StorageDict* to store the some persistent results with the following definition: + +.. code-block:: python + + from hecuba import StorageDict + + class Results(StorageDict): + ''' + @TypeSpec dict <, x:double, y:double, z:double> + ''' + + experiment = Results("breathing.results") + +And by mixing both definitions we can write a small application. Note that the ``Dataset`` class has an attribute ``particles`` referencing the class ``ParticlesPositions``. + +.. code-block:: python + + from hecuba import StorageObj, StorageDict + import numpy as np + + + + class ParticlesPositions(StorageDict): + ''' + @TypeSpec dict <, x:double, y:double, z:double> + ''' + + + class Dataset(StorageObj): + ''' + @ClassField author str + @ClassField open_access bool + @ClassField injected_particles int + @ClassField geometry numpy.ndarray + @ClassField particles ParticlesPositions + ''' + + + dt1 = Dataset("breathing.case235") + + dt1.author = "BSC" + dt1.open_access = True + dt1.injected_particles = 250000 + dt1.geometry = np.load("./geom_file.npy") + + for part_id in range(dt1.injected_particles): + dt1.particles[part_id] = list(np.random.random_sample(3,)) + +By passing a name of type ``str`` to the initializer of a Hecuba class instance, the object becomes persistent and sends the data to the persistent storage. Said name will act as an identifier for its data and other objects created with the same name will access the same data. In this way, if we pass a name which was previously used to create an object we will retrieve the previously persisted data. + +Initializing an instance of an hecuba class without a name results in a regular in-memory object. However, its data can be persisted at any moment by calling the instance method *make_persistent*, provided and implemented by all Hecuba classes. +This method expects a ``str`` name, in the same way the initializer did, and will be used to identify the data in the future. This method will send the data to the data store, mark the object as persistent, and, future accesses will access the data store if deemed necessary. + +.. code-block:: python + + class ParticlesPositions(StorageDict): + ''' + @TypeSpec dict <, x:double, y:double, z:double> + ''' + + r=ParticlesPositions() + + r.make_persistent("OutputData") diff --git a/docs/source/basics.rst b/docs/source/basics.rst new file mode 100644 index 00000000..02034226 --- /dev/null +++ b/docs/source/basics.rst @@ -0,0 +1,391 @@ +.. _basics: + +The basics +========== + +In this chapter we describe in detail the interface provided by the Hecuba. We also illustrate how the supported data types and operations can be applied. + +Supported Data Types and Collections +************************************ + +Immutable types supported: +-------------------------- + +Data types +^^^^^^^^^^^ + +* ``str``, ``bool``, ``decimal``, ``float``, ``int``, ``blob``, ``tuple``, ``buffer``. + +* ``double`` floating point numbers will be stored as double precision numbers. + +Collections +^^^^^^^^^^^ + +* ``numpy.ndarray``. +* ``frozenset`` supported in StorageObj only. + +Mutable collections supported: +------------------------------ + +* ``dict``. + +* ``set`` Subject to restrictions, supported only by StorageDict (development underway). + +* ``list`` to group a set of values for a given key in a StorageDict. E.g. ``dict[0] = [1,2,3]``. + +Hecuba Data Classes +******************* + +Storage Object +-------------- + +The *StorageObj* is the simplest abstraction provided by Hecuba. It acts like a ``namedtuple``, or a ``dataclass``, where the user can define attributes and access them. However, in this case, the user can choose which attributes will be persisted to the data store. + +To declare instances of the StorageObj, the user first needs to define a class inheriting from the *StorageObj* as well as define the data model of the persistent attributes. The format of the data model is a Python comment with one line per attribute. Each line must start with the keyword *@Classfield* and continue with the name of the attributes and its data type specification. + +.. code-block:: python + + class ClassName(StorageObject): + ''' + @ClassField attribute_name attribute_type + ''' + +For example, the following code shows the definition of a class containing an attribute of type integer. + +.. code-block:: python + + class MyClass(StorageObj): + ''' + @ClassField MyAttribute_1 int + ''' + +When the user needs to use collections as attributes, the syntax needs to be further elaborated. For example, to define a Python dictionary it is necessary to specify the type of the keys and the type of the values. In this case, after the attribute type we can find the rest of the specifications within angular brackets. + +.. code-block:: python + + class ClassName(StorageObj): + ''' + @ClassField attribute_name attribute_type + ''' + +For example, the following code adds a dictionary attribute: the key is of type ``Integer`` and the value a ``str``. + +.. code-block:: python + + class MyClass(StorageObj): + ''' + @ClassField MyAttribute_1 int + @ClassField MyAttribute_2 dict <, str> + ''' + +Each additional level required to complete a specification type can be added within angle brackets. For example, the following code adds the specification of a dictionary that has a key of type tuple, which is composed of an ``Integer`` and a ``str``, and that has a value of type ``Integer``. + +.. code-block:: python + + class MyClass(StorageObj): + ''' + @ClassField MyAttribute_1 int + @ClassField MyAttribute_2 dict <, str> + @ClassField MyAttribute_3 dict <, int> + ''' + +Attributes of type ``dict`` allow the programmer to assign a name to each component of the dictionary (keys and values). These names can help users to give semantic meaning to the data, for instance when accessing the results of a dictionary or when exploring the persistent data with external tools. + +.. code-block:: python + + class MyClass(StorageObj): + ''' + @ClassField MyAttribute_1 int + @ClassField MyAttribute_2 dict <, str> + @ClassField MyAttribute_3 dict <, int> + @ClassField MyAttribute_4 dict <, myvalue:int> + ''' + +Storage Dictionary +------------------ + +The *StorageDict* abstracts the underlying data model and exposes the user interface of a python ``dict``. The mechanism to create instances of a *StorageDict* is the same as the *StorageObj*. A class that inherits from the *StorageDict* must be defined, and an annotation describing the data model of the keys and values added. + +The data model definition must start with the keyword *@TypeSpec* and continue with the type of the keys, and the values. + +.. code-block:: python + + class ClassName(StorageDict): + ''' + @TypeSpec dict <values_specification> + ''' + +For example, the following code shows the definition of a dictionary with one key of type ``Integer`` and a ``str`` value. + +.. code-block:: python + + class MyClass(StorageDict): + ''' + @TypeSpec dict<, str> + ''' + +Also, the user can set names to the keys and values to give semantic meaning. +It might be desirable to access the results of a dictionary by their name, or when exploring the persistent data with external tools. + +.. code-block:: python + + class MyClass(StorageDict): + ''' + @TypeSpec dict<, myvalue:str> + ''' + +Additional keys or values can be added to a *StorageDict* definition. + +.. code-block:: python + + class MyClass(StorageDict): + ''' + @TypeSpec dict<, myvalue1:int, myvalue2:int, myvalue3:str> + ''' + +Distributed sets inside a StorageDict +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The usage of distributed sets is a bit restricted. +When they a set is used in a StorageDict, the persistent object cannot have more attributes than a single set. +The set contains all the persistent storage funcionalities for sets, for example: + +.. code-block:: python + + from hecuba import StorageDict + class DictWithSet(StorageDict): + ''' + @TypeSpec dict<, s1:set> + ''' + + my_data = DictWithSet("my_app.my_data") + my_data["1", 1] = {1} + my_data["2", 2] = {1, 2, 3} + my_data["2", 2].remove(2) + other_data = DictWithSet("my_app.other_data") + other_data["2", 2] = my_data["2", 2].union(my_data["1", 1]) + for key, set_value in other_data.items(): + if not 2 in set_value: + other_data[key].add(2) + +Cross-class referencing +----------------------- + +A previously defined class can be referenced in the definition of a newer class. +For instance, a custom StorageObj can have an attribute of type "MyClass", and the latter, be a custom class that inherits from a StorageObj or StorageDict. + +The same is possible the other way around, a StorageDict can have as value(s) other StorageDicts or StorageObjs. +In order to do so, the programmer needs to specify the data model of both: + +.. code-block:: python + + # file is named classes.py + from hecuba import StorageDict, StorageObj + class MyObj(StorageObj): + ''' + @ClassField a int + @ClassField b str + ''' + + class MyDict(StorageDict): + ''' + @TypeSpec dict<, my_obj:classes.MyObj> + ''' + + my_dict = MyDict("my_app.my_data") + obj1 = MyObj() + obj1.a = 2 + obj1.b = "hello" + my_dict[0] = obj1 + +Storage Numpy +------------- + +With the *StorageNumpy* class programmers can instantiate numpy ndarrays that eventually can be persisted. +Using the StorageNumpy class there is no need to define any additional class, the user can use this Hecuba class directly in the code to instantiate numpys ndarrays. +The shape of the array in inferred from the data assigned. +Programmers can instantiate volatile numpy ndarrays and make them persistent later or can instantiate persistent numpy ndarrays. +The initial value for the StorageNumpy must be passed as a parameter of the constructor. +The following fragment of code shows the different options to instantiate a StorageNumpy: + +.. code-block:: python + + from hecuba import StorageNumpy + import numpy as np + n = StorageNumpy(np.arange(10).reshape(2,5)) # n is a volatile StorageNumpy + n = StorageNumpy(np.arange(10).reshape(2,5), "persistent) # n is a persistent StorageNumpy + +Once instantiated, the programmer can user the functions of the numpy library to manipulate the StorageNumpys. Hecuba retrieves from disk (if needed) the values of the numpy ndarrays: + +.. code-block:: python + + from hecuba import StorageNumpy + import numpy as np + A=StorageNumpy(np.arange(10).reshape(2,5), "matrixA") + B=StorageNumpy(np.arange(10).reshape(5,2), "matrixB") + res=np.dot(A,B) #res is a voltile StorageNumpy that programmers can persist if needed + +Persistent StorageNumpy are store distributed in the database. +They are splitted in blocks, transparently to the programmer. +Hecuba assigns to each block an identifier that will act as the key of the block and will decide which node holds it. + +Hecuba Classes instantiation +**************************** + +Hecuba provides two different constructors to instantiate *StorageNumpys* and classes that inherit from *StorageObjs* and *StorageDicts*. +The first one is to instantiate new objects that have no persistent data associated, and data will be kept in-memory until the instance method *make_persistent* is called. + +The second constructor is to instantiate objects that will make use of the persistent storage. +In this case, the constructor receives a string as the parameter, which is the identifier of the data inside the data store. +Hecuba checks if there already exists some persistent data with that identifier, and if it doesn’t exist Hecuba creates it. + +If the identifier is already used in the data store, then Hecuba checks if the schema of that existing object matches with the object that the programmer is trying to instantiate. +If this is the case, Hecuba assumes that the programmer wants to get access to that object and completes the instantiation: +any access will be performed on the existing object. +If the schema does not match, the user code will fail with an exception. + +Hecuba allows to deal with hierarchical namespaces and to specify several levels of the hierarchy with just one identifier. +For example, directory name and file name, in the case of file systems, or keyspace name and table name, in the case of Apache Cassandra. +The format of the identifier specifies that a dot must separate the identifiers of each level in the namespace. +If the identifier does not contain a dot then Hecuba interprets that the identifier refers just to the name in the lowest level in the namespace hierarchy +(there are default values for the rest of the components to identify the data that the user can configure through environment variables, see section Hecuba configuration parameters). + +.. code-block:: python + + o1=MyClassName() # o1 is a volatile object + + o2=MyClassName("Table") # o2 is a persistent object: the name of the table is "Table" and the keyspace is the default name used in this execution + + o3=MyClassName("Keyspace.Table") # o3 is a persistent object: the name of the table is "Table" and the name of the keyspace is "Keyspace" + +It is also possible to use the static method *get_by_alias* to instantiate an already existing persistent object. + +.. code-block:: python + + o4=MyClassName.get_by_alias("Keyspace.Table") + +Notice that Hecuba registers the schema of the user defined classes and thus, it is not possible to reuse those class names for a different class definition. The access code to an instance of such a redefined class will fail due to schema mismatch. + +Access to Hecuba objects +************************ + +From the point of view of the programmers, both objects with persistent data and objects without persistent data are accessed in the same way: like regular Python objects. +However, Hecuba intercepts all accesses to a Hecuba object and executes the suitable code to refer to the involved data. +Notice that some accesses to persistent data may be solved without accessing the data store because Hecuba implements a cache to keep recently used persistent data and thus, save accesses to the data store. + +.. code-block:: python + + o1=MySOClass() # instantiation of an object without persistent data + o1.dict_attr[0]=1 # access to a regular Python object in memory + value1=o1.dict_attr[0] # access to the data store to retrieve the data + value2=o1.dict_attr[0] # access to Hecuba cache in memory + o2=MySOClass("Table") # instantiation of persistent object + o2.dict_attr[0]=2 # saved to Hecuba cache in memory, to be stored in the database later + +Hecuba allows to define *StorageDicts* with more than one value. +This is implemented as a named tuple and, thus each component of the value can be referred with the name assigned in the class specification or with its positional value. + +.. code-block:: python + + class MyClass(StorageDict): + ''' + @TypeSpec dict<, myvalue1:int, myvalue2:int, myvalue3:str> + ''' + + d=MyClass("dictname") # dictname represents an already existing persistent StorageDict + i=d[0,"value"].myvalue2 # access to the second attribute of the value corresponding with key (0,"value") + i=d[0,"value"][1] # access to the second attribute of the value corresponding with key (0,"value") + +Making volatile data persistent +******************************* + +All Hecuba volatile objects can become persistent at any point. +The programmer only needs to use the *make_persistent* method passing as parameter which will be the identifier of the object in the data store. +If the volatile object already contains data, all the data is eventually sent to the data store. +And from this point on, all the modifications on the object will be considered to be persistent. + +.. code-block:: python + + o1 = MyObj() + o1.a = 2 + o1.b = "hello" + o1.make_persistent("myksp.mytable") + +If the identifier is already used in the data store, then Hecuba checks if the schema of that existing object matches with the object that the programmer is trying to persist. +If this is the case, then the persisting operation concludes successfully and the data is sent to the data store. +If the schema does not match, then the code of the user fails with an exception. + +Synchronizing with data store +***************************** + +Hecuba implements some optimizations in the interaction with the database as caching and prefetching. +This means that, even an object is defined to be persistent, its contents may be in memory. +More over, it implements asynchronous writes to allow overlapping a computing phase of the application with the access to the data store and to reduce the number of interactions with the data store. +That is, it is possible that during some time the persistent content of an object may be only in memory. +The programmer can force at any moment the actual sending of the data to the data store using the *sync()* method. +Notice that when a persistent object is deallocated (by the garbage collector), the sync method is automatically called, so before the process ends the data is guarantee to be coherently stored in the database. + +.. code-block:: python + + o1 = MyClass("myname") + o1.myattr = 4 + o1.sync() # this method guarantees that data is stored in the database so if other process instantiates it will access the data up to date + +Methods for Iterating +********************* + +In order to support data partitioning, Hecuba classes implement the method *split*. +This method returns an iterator of partitions, where each partition is a new object of the same class containing just a part of the base object. +Using the *split* method no data loading from storage happens until the data in the partition is accessed. + +Partitioning of a dataset was introduced to support the implementation of data-driven distributed applications: developers can define parallel tasks each of them working on one of these chunks of data. +Hecuba supports an additional level of iteration that allows iterating over each of these partitions, using the python iteration methods. + +.. image:: images/HecubaIterators_2.png + +The current implementation of the *split* method does not supports partitioning on volatile objects. + +The current criteria to create the partitions is oriented to favor the load balancing between processors and to enhance data locality. +Thus, the partition is considered the unit of work and the *split* method creates enough partitions to facilitate a balanced work assignment between processors. + +This method is only implemented for *StorageDicts* and *StorageNumpys* as they are the classes intended to have a big collection of data. +Notice that if a *StorageObject* contains some attributes of these classes then it is possible to partition each of this collection using their own class method. + +Following, we describe the specificities of this method for *StorageDict* and for *StorageNumpy*: + +* **Iterating over a StorageDict**: Hecuba takes into account the location of all data across the distributed storage system and assigns to each partition of a *StorageDict* only data that resides on the same node. This way the task scheduler has the opportunity to consider data location as a factor when taking task assignment decisions. Currently, the number of partitions is a parameter that the user can configure to tune the load balancing (see section Hecuba configuration parameters). As part of our future work, we plan to automate the calculation of this value. + +.. code-block:: python + + # sd is the instance of a persistent *StorageDict* + for sd_partition in so.sd.split(): # Iterator on blocks + # Here we'll have access to each partition of the StorageDict + for key in sd_partition.keys(): # Iterator on elements of a block using the python method *keys* + do_something(key) + +* **Iterating over a StorageNumpy**: by default, each partition of a *StorageNumpy* corresponds with a *StorageNumpy* block. In the current implementation, the size of the block is fixed but in future releases it will be a configurable parameter. The distribution of blocks in the storage follows the z-order algorithm, to enhance a uniform distribution. In the case of the 2-dimmensional *StorageNumpys*, the split method supports a parameter (which is ignored in the case of the *StorageDicts* and *StorageNumpys* with a different number of dimmensions). This parameter is intended to support the two typical pattern of accesses to matrices: by rows and by columns. Using this parameter, each partition is either composed of a column of blocks (parameter cols=False) or composed of a row of blocks (parameter cols=True). + +.. image:: images/SNSplit_3.png + +.. code-block:: python + + # sn is the instance of a persistent StorageNumpy + for sn_block in sn.split(): # Iterator on blocks + for elt in sn_block: # Iterator on numpy ndarrays + #do something with the numpy element + + # if sn is the instance of a persistent 2D-StorageNumpy it is possible to use the cols parameter + for sn_block in sn.split(cols=True): # Iterator on blocks. Each partition is a column of StorageNumpy blocks + for elt in sn_block: # Iterator on numpy ndarrays + #do something with the numpy element + +Deleting data from the data store +********************************* + +In order to delete a persistent object from the data store, Hecuba provides the method *del_persistent*. +This method deletes all data and meta-data associated to the specified object. + +.. code-block:: python + + o1.o1dict.del_persistent() + o1.del_persistent() \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 00000000..ad9a102f --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,27 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'Hecuba' +copyright = '2024, Hecuba Developers' +author = 'Hecuba Developers' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [] + +templates_path = ['_templates'] +exclude_patterns = [] + + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'sphinx_rtd_theme' +html_static_path = ['_static'] diff --git a/docs/source/conf_param.rst b/docs/source/conf_param.rst new file mode 100644 index 00000000..774287e4 --- /dev/null +++ b/docs/source/conf_param.rst @@ -0,0 +1,51 @@ +.. _conf_param: + +Hecuba Configuration Parameters +=============================== + +There are several parameters that can be defined when running our application. The basic parameters are the following: + + + +* CONTACT_NAMES (default value: 'localhost'): list of the Storage System nodes separated by a comma (example: export CONTACT_NAMES=node1,node2,node3) + +* NODE_PORT (default value: 9042): Storage System listening port + +* EXECUTION_NAME (default value: "my_app’): Default name for the upper level in the app namespace hierarchy + +* CREATE_SCHEMA (default value: True): if set to True, Hecuba will create its metadata structures into the storage system. Notice that these metadata structures are kept from one execution to another so it is only necessary to create them if you have deployed from scratch the storage system. + + +Hecuba Advanced Configuration Parameters +**************************************** + +* NUMBER_OF_BLOCKS (default value: 1024): Number of partitions in which the data will be divided for each node + +* CONCURRENT_CREATION (default value: False): you should set it to True if you need to support concurrent persistent object creation. Setting this variable slows-down the creation task so you should keep it to False if only sequential creation is used or if the concurrent creation involves disjoint objects + +* LOAD_ON_DEMAND (default value: True): if set to True data is retrieved only when it is accessed. If it is set to False data is loaded when an instance to the object is created. It is necessary to set to True if you code uses those functions of the numpy library that do not use the interface to access the elements of the numpy ndarray. + +* DEBUG (default value: False): if set to True Hecuba shows during the execution of the application some output messages describing the steps performed + +* SPLITS_PER_NODE (default value: 32): Number of partitions that generates the split method + +* MAX_CACHE_SIZE (default value: 1000): Size of the cache. You should set it to 0 (and thus deactivate the utilization of the cache) if the persistent objects are small enough to keep them in memory while they are in used + +* PREFETCH_SIZE (default value: 10000): Number of elements read in advance when iterating on a persistent object + +* WRITE_BUFFER_SIZE (default value: 1000): size of the internal buffer used to group insertions to reduce the number of interactions with the storage system + +* WRITE_CALLBACKS_NUMBER (default value: 16): number of concurrent on-the-fly insertions that Hecuba can support + +* REPLICATION_STRATEGY (default value: 'SimpleStrategy'): Strategy to follow in the Cassandra database + +* REPLICA_FACTOR (default value: 1): The amount of replicas of each data available in the Cassandra cluster + +Hecuba Specific Configuration Parameters for the *storage_props* file of PYCOMPSs +********************************************************************************* + +* CONTACT_NAMES (default value: empty): If this variable is set in the storage_props file, then COMPSs assumes that the variable contains the list of of an already running Cassandra cluster. If this variable is not set in the storage_props file, then the enqueue_compss command will use the Hecuba scripts to deploy and launch a new Cassandra cluster using all the nodes assigned to workers. + +* RECOVER (default value: empty): if this variable is set in the storage_props file, then the enqueue_compss command will use the Hecuba scripts to deploy and launch a new Cassandra cluster starting from the snapshot identified by the variable. Notice that in this case, the number of nodes used to generate the snapshot should match the number of workers requested by the enqueue_compss command. + +* MAKE_SNAPSHOT (default value: 0): the user should set this variable to 1 in the storage_props file if a snapshot of the database should be generated and stored once the application ends the execution (this feature is still under development, users can currently generate snapshots of the database using the c4s tool provided as part of Hecuba). diff --git a/docs/source/cplusplus.rst b/docs/source/cplusplus.rst new file mode 100644 index 00000000..1b9d1ba8 --- /dev/null +++ b/docs/source/cplusplus.rst @@ -0,0 +1,293 @@ +.. _cplusplus: + +Access to Hecuba from C++ +========================= + +We have added a layer that allows the interaction with the Hecuba core layer, without using the Python interface. This C++ interface is work in progress and currently does not cover all the functionality implemented by Hecuba. In addition, we expect to improve the usability of the interface in the next releases. + +Class declaration +***************** + +Currenly the C++ interface of Hecuba supports three types of Hecuba Objects: + +* StorageDict +* StorageObject +* StorageNumpy + +Programmers can define any class inheriting from one of these classes. The method to define each class depends of the base class. + +Classes derived from StorageDicts +--------------------------------- + +StorageDict is a templatized class with a template composed of three elements: the class of the key, the class of the value and the name of the derived class. + +The class of the key is the templatized class KeyClass defined by Hecuba, where the template is composed of a variable number of types: one for each element of the key. The following sentences define an alias for three different key classes with different number of attributes of different types: + +.. code-block:: cpp + + using Key1 as KeyClass + using Key2 as KeyClass + using Key3 as KeyClass + +The first element of the key acts as partition key and the rest acts as clustering keys. + +The class of the value is the class ValueClass also defined by Hecuba and also with a variadic template: one type for each element of the value. For example: + +.. code-block:: cpp + + using Value1 as ValueClass + using Value2 as ValueClass + +The types of both keys and values can be basic types or other Hecuba derived classes. + +Thus, the following sentence defines a StorageDict that has one integer as key and a string as value: + +.. code-block:: cpp + + class myDictClass:public StorageDict { + + // can be empty + + } + +Classes derived from StorageObjects +----------------------------------- + +Hecuba provides the macro HECUBA_ATTRS to define the persistent attributes of classes derived from StorageObjects. The parameters of this macro are a comma separated pairs composed by type of the attribute and the name of the attribute. The number of persistent attributes is currently limited to 1024. The following sentence defines a class derived from a Storage Object with three attributes: "attr1" of type uint64_t, "attr2" of type std::string and "attr3" of type int32_t: + +.. code-block:: cpp + + class myObjClass:public StorageObject{ + + HECUBA_ATTRS ( + uint64_t, "attr1", + std::string, "attr2", + int32_t, "attr3" + ) + } + +Classes derived from StorageNumpy +--------------------------------- + +At this moment, Hecuba does not support any specialization of the StorageNumpy, for this reason, the C++ interface of Hecuba does not support the definition of derived classes from StorageNumpy. Programmer can instantiate StorageNumpys without any additional class definition. + +Supported basic types +--------------------- + +Currently the basic types that C++ Hecuba supports are: + +* int32_t, float, double, std:string, bool, char, int64_t. + +New objects instantiation +************************* + +The object instantiation is as any regular object: + +.. code-block:: cpp + + myObjClass o; + myDictClass d; + StorageNumpy sn; + +The only consideration is that if the programmer implements in the derived class a constructor with parameters, that constructor should explicitly call the default constructor of the base class. + +In the case of the instantiation of a StorageNumpy, Hecuba implements an additional constructor that allows to set the numpy data during the instantiation. The signature of this constructor is: + +.. code-block:: cpp + + StorageNumpy::StorageNumpy(void *data, vector metas); + +And this constructor is invoked in a declaration like the following one: + +.. code-block:: cpp + + StorageNumpy sn (data, metas); + +Where data is a pointer to a memory region with the content of the numpy in C order. Keep in mind that current implementation of StorageNumpy only support numpy.ndarrays of elements of type float. And metas is a vector that contains the size of each dimension of the numpy.ndarray. + +Persisting new objects +********************** + +Once an object is instantiated, the first step is to persist it. The connection with the database is performed when the first Hecuba object is persisted. And this connection will be active during all the execution of the process. The method to persist an Hecuba object is make_persistent: + +.. code-block:: cpp + + void make_persistent(std::string name) + +The parameter the name of the persistent object (a string that will identify the persistent object in the database): + +This operation will prepare in the database the tables that will contain the object data. The operation to persist a Hecuba object is the same for all types of Hecuba base object. + +.. code-block:: cpp + + d.make_persistent("mydictname"); + +All the insertions in a persisted object are sent asynchronously to the database. + +At this moment it is not possible to insert data in a volatile Hecuba object but in the next releases we will extend this functionality. + +The method make_persistent also generates a python file with the class definition of the object (for StorageDict and StorageObjects), that can be used from any python code that needs to access this persistent object. + +Retrieving already existing persistent objects +********************************************** + +Hecuba implements the method getByAlias to connect with a previously persisted Hecuba Object: + +.. code-block:: cpp + + void getByAlias(std::string name) + +The parameter of this method is the name of the object (the one used in the persisting operation). + +.. code-block:: cpp + + myDict d; + + d.getByAlias("mydictname"); + +Object Access +************* + +The interface to access Hecuba objects depends on the Hecuba base class. + +Accessing StorageDicts +---------------------- + +In the case of StorageDict the access is implemented with the same operator of C++ maps or vectors: Hecuba overrides the indexing operator ([])). + +In the insertion operation, the user has to specify the element of type KeyClass that acts as the index, and the element of type ValueClass that needs to insert. For example, if Key is an alias for a KeyClass composed of two elements of type int32_t and Value an alias for a ValueClass composed of three elements of type int32_t, std::string, and float, the following sentence represents a valid insertion in d: + +.. code-block:: cpp + + d[Key(1,2)] = Value(3,"hi",(float)3.14) + +In the read operation, the indexing operator returns a Value object. To facilitate the extraction of each element of the Value we have implemented the same interface that offers the standard tuples of C++. The following sentence will return the first element of Value + +.. code-block:: cpp + + Value v = d[Key(1,2)]; + int32_t v1 = Value::get<0>(v); + std::string v2 = Value::get<1>(v); + float v3 = Value::get<2>(v); + +The C++ interface of Hecuba also implements an iterator on the keys of a persistent StorageDict. The following loop accesses all the elements of a StorageDict: + +.. code-block:: cpp + + Key k; + Value v; + for(auto it = d.begin(); it != d.end(); it++) { + k=*it; + v=d[k]; + } + +Accessing StorageObjects +------------------------ + +In the case of StorageObjects the operator to access the attributes is the same than to access attributes of regular C++ objects: Hecuba overrides the accessing operator (.). If the user instantiates a StorageObject with one attribute named attr1 of type int32_t, then the following sentence will assing 1 to the attribute attr1: + +.. code-block:: cpp + + o.attr1 = 1; + +And the following sentence will read attr1 from o: + +.. code-block:: cpp + + int32_t v_read = o.attr1; + +Accessing StorageNumpys +----------------------- + +In the case of StorageNumpy the current implementation of C++ interface of Hecuba only supports the insertion of the whole numpy data. The insertion can be performed in two ways: + +* During the instantiation of the StorageNumpy, using the constructor that receives both the data and the metadata. + +* Using the method setNumpy. The signature of this method is: + +.. code-block:: cpp + + StorageNumpy::setNumpy(void *data, vectormetadata) + +Where data is a pointer to a memory region with the content of the numpy.ndarray in C order and meta is a vector with the size of each dimension of the numpy.ndarray. + +Synchronization with disk +************************* + +All the insertions in persistent Hecuba objects are sent asynchronously to the database. Hecuba guarantees that at the end of the session all the data will be up to date in the database. However, Hecuba offers a method to explicitly synchronize an object with the database. The signature of this method is the following: + +.. code-block:: cpp + + void sync() + +And can be used with any type o persistent Hecuba objects: + +.. code-block:: cpp + + d.sync(); + +Nested Objects +************** + +Both StorageDicts and StorageObjects can contain other Hecuba objects, using the class name to declare the attribute. + +For example, a StorageDict indexed with integers and with values of type StorageNumpy could be defined as follows: + +.. code-block:: cpp + + using Key as KeyClass; + using Value as ValueClass; + class nestedDict : public StorageDict{ + } + +And then used as follows: + +.. code-block:: cpp + + nestedDict nd; + nd.make_persistent("nd_dict"); + Key k(0); + StorageNumpy sn(data,metadata); //data and metadata are variables + sn.make_persistent("mynumpy"); //initialized properly + Value v(sn); + nd[k]=v; + +We can use this dictionary as an attribute of an object: + +.. code-block:: cpp + + class nestedObject: public StorageObject{ + public: + HECUBA_ATTRS( + std::string, description, + nestedDict, content + ) + } + +And use it: + +.. code-block:: cpp + + nestedObject no; + no.make_persistent("nested_object"); + no.description = "numpys generated today"; + no.content = nd; + +Compiling C++ applications using Hecuba +*************************************** + +Assuming that the variable HECUBA_ROOT contains the path where Hecuba is installed, applications using the C++ interface of Hecuba should be compiled using the following compiling command: + +.. code-block:: bash + + g++ -o application \ + application.cpp \ + -std=c++11 \ + -I ${HECUBA_ROOT}/include \ + -I ${HECUBA_ROOT}/include/hecuba \ + -L${HECUBA_ROOT}/lib \ + -lhfetch \ + -Wl,-rpath,${HECUBA_ROOT}/lib \ + -Wl,-rpath,${HECUBA_ROOT}/../Hecuba.libs + +This compilation command works both for the standalone C++ Hecuba installation and for the python Hecuba installation where the HECUBA_ROOT Variable should point to the directory that contains the python Hecuba package. \ No newline at end of file diff --git a/docs/source/deployment.rst b/docs/source/deployment.rst new file mode 100644 index 00000000..b679de71 --- /dev/null +++ b/docs/source/deployment.rst @@ -0,0 +1,23 @@ +.. _deployment: + +Hecuba Deployment +================= + +The first step is to download the source code from the following repository: +https://github.com/bsc-dd/hecuba. The directory structure is the following: + +* main folder: contains the following files and sub-folders: + + * **README.md**: helpful information for the installation and execution. + + * **setup.py**: Hecuba installer. + + * **requirements.txt**: requirements needed by Hecuba. + + * **hecuba_py** folder: contains the Hecuba python code. Should not be modified by users. + + * **hecuba_core** folder: contains the Hecuba C++ code. Should not be modified by users. + + * **tests** folder: contains the Hecuba tests. Should not be modified by users. + +Find the instructions for the `Hecuba Installation Procedure `_. diff --git a/docs/source/exe_pycompss.rst b/docs/source/exe_pycompss.rst new file mode 100644 index 00000000..7021ced2 --- /dev/null +++ b/docs/source/exe_pycompss.rst @@ -0,0 +1,21 @@ +.. _exe_pycompss: + +Containerized execution with PyCOMPSs +===================================== + +Hecuba offers support to execute Cassandra in a singularity container. +At this moment, this support is only available through the execution with PyCOMPSs. +To activate this type of execution, the user has to set the enqueue_compss option *storage_container_image* to the value *default*. +This option requires a valid singularity Cassandra file to be in the directory $HECUBA_ROOT/singularity. +In the next release, we plan to extend this functionality to use this *storage_container_image* option to specify a different path for the singularity file. + +.. code-block:: bash + + PATH_TO_COMPSS_INSTALLATION/enqueue_compss \ + --num_nodes = 4 \ + --storage_props = storage_props.cfg \ + --storage_home=$HECUBA_ROOT/compss/ \ + --scheduler=es.bsc.compss.scheduler.fifodatanew.FIFODataScheduler \ + --storage_container_image=default \ + --lang=python \ + $(pwd)/myapp.py diff --git a/docs/source/execution.rst b/docs/source/execution.rst new file mode 100644 index 00000000..8cd9acbf --- /dev/null +++ b/docs/source/execution.rst @@ -0,0 +1,38 @@ +.. _execution: + +How to execute +============== + +Sequential applications +*********************** + +To run a sequential application using Hecuba is really simple after the requirements have been satisfied. We just need to execute the application with python, and it will use the configured data store: + +.. code-block:: bash + + python3 myApp.py + +This command assumes that you have a running a local Cassandra instance. +If you need to connect to a different instance of Cassandra you must set the variables *CONTACT_NAMES* and *NODE_PORT* with the suitable values +(see `Hecuba Configuration Parameters `_). + +Parallel applications on queue based systems +******************************************** + +To run a parallel Hecuba application using PyCOMPSs you should execute the *enqueue_compss* command setting the options storage_props and *storage_home*. +The *storage_props* option is mandatory and should contain the path of an existing file. +This file can contain all the Hecuba configuration options that the user needs to set (can be an empty file). +The *storage_home* option contains the path to the Hecuba implementation of the Storage API required by COMPSs. +Following, we show an example of how to use PyCOMPSs and Hecuba to run the python application in the file myapp.py. +In this example, we ask PyCOMPSs to allocate 4 nodes and to use the scheduler that enhances data locality for tasks using persistent objects. +We assume that the variable *HECUBA_ROOT* contains the path to the installation directory of Hecuba. + +.. code-block:: bash + + PATH_TO_COMPSS_INSTALLATION/enqueue_compss \ + --num_nodes = 4 \ + --storage_props = storage_props.cfg \ + --storage_home=$HECUBA_ROOT/compss/ \ + --scheduler=es.bsc.compss.scheduler.fifodatanew.FIFODataScheduler \ + --lang=python \ + $(pwd)/myapp.py \ No newline at end of file diff --git a/docs/source/images/DataLocality_4.png b/docs/source/images/DataLocality_4.png new file mode 100644 index 0000000000000000000000000000000000000000..60b0ef274afc5b75b435d05547d393422b5cad0e GIT binary patch literal 47399 zcmZsDb9g0Bw{FadZQGM%V%xSgadvFmww;M>+sTBJiL+zdy7_(QJKs6?x%ZFmr>k~% zSNE!_RcpQP+dD#0UIGCQ7Y+mj1VKttR2c*W>=^_EloAH&tLBL}j_9jEWho+}Xe}Zk z0(l*7ZLl@0wy z`iA_4+M3Y@x+jp{O%4dyM-rYYP75#!Km-T> zR1HqgBmocd=La9`;TkUyGgCCd&WOgrhPehsHXh&IkYRV^GR}el)1;xdj0c1d$RIQgsJC(}VU_ReShI zl=ZyqMiyo&fP<4{A_X(X(mSoNW;)egZmIE`?05*oK$$G0hATuD`#Md8`?y

f>;I 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⪼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�SV;wLvY%+)g$;lS2L4cXPFGA*oK{^J$wn9q2ZjYRp}vp#)xhSy z^vKt11;$=d79pKm$D$FLv3<0m4>V+bkwW^On%*jsNK&bZdeIpH-!lq{*(gl_WHG{M zJ3kM$u1M05J?#&~@}gc-KE$yPO&`9zb6uvGwDfok#6GHK6FEMzQiv%q+bg2lsZSen zZ9FrX8L{OH;!|136o2IBe{MRR5JoL@*#CO=AD7*&abZ+k{^=n*5iM!BM06HGe_h+0 zXrV_(b>GKX`xFzYD1g*s-dw{Gv{_%#MO_xpX_k?;cc8WIB%cmsBmEy=h6X@`pN>Zl zSRa^*0WP@cewV#jP2C;G^0l9tESVfEKIb&-C>JEt6ifiC643LC{RiBq*0}iUFQ_BXt7Q%hE)^eBe7B0NJx#sxi?g{$p#zuUdSyeNy zb6}_Yw!f7M4+MilnUgKSD@%_t%19PMkz)5p!!M5lndK!T7226T$|rN_8a2kh8{OgL zVaH7{7ambnYC7E{cX1ROka9*P7m66 z#x-=71>sBKArE&-4ikT`NEig@0Y;&IC%=yk218kYYplx=|8{t4Tf>a9GJ0k=zAZhH z^YZ|bgg`cEtSnUW$c$wQrAEguum4sa;$NulM$F$Ukl(V%cM?o7^sB-&IK6@hB zivXzI+U*JQKX5lh&XVjfBH#R`d236Jwi;m%ma~rcih=_AtHv0=iAL)F$P9r1X@`xP zLHM^rxn!iJ-Zd840(NnbA=G@{`wCpiaj*HfrD>{oc|Vvj!2#GC64DoYQ9!?8G7`If z?(Pp4uM=|=doSQXq632~Ge*ly>gT5jI#ij~Fz_kXtVj;=8wX*~vzcWtpbC(%fXb^f zsMf8IvF%QtOt$9)tn=@DWpt$Yr7><9>d%Ca}d<3Hikf8W4 znou~bVm4gvQDyo^4U)hF$pUc*j7n;(_n4G6&3Q|_+*kUuzUbj&%4BG8584~1w_YrJW-c;C;*4Wg6+ zQ-Te@H!S?yH6*3cCP__D7R0boTL@v$k5?sC_uLf@Phxe|;A3JUJ&yXqcZ3Hq3ek{`up>n;0d1`*Y0Ty=Q4@ zM5xNK(0*YXAt6zMm2I{0)6*w*CX$x!d={>+pYq9jk66i!G#X-}Xq^UTrJK?i_+~$+ z$Z&~UZud(uvsCK!`@h4*%^Jl@!juy0ph>6@!3`gi0yvDQqR9sBZ66K5MuCoiv_i`U^TY zouP}BzQ`=?9*B>d|I$%?eYIsu)8GCU&o?d`uXeC`HE^+x7AglY3X_BYKkMD^8~V%b z>d@WL(++4+F=`kYIE8u{%?m;h3<-*bzd9Y|u^MZc^zOh;=ESW88#mseTN)3@3^*x+ zre^|%#803Ps;aeF^P9F3d9}^ShLd8Swo z*QY*2FSa7zU<2}kCxBce(Jv9;Axdb(~D! zZc#GgbYq2-?vsA%bL&-DXvYjD=oQwkpr2pMgH1;NEOH$WOA&IDr#m1V>LA#3zU2`q ze_7LHRG#vSEL9f$NUEEM)dvsUf;W+lGo@7Z-RQzMO< zQ?5i)C@|>|#i2bZsC$7x2QUQn#kgo(E3H%Z9)EoQ*N^mIeJ6lw+8qb;+zBB%`u23u z1bkgTRnYj2$)z8ciBmKGw)Xk7>n9`CXE44Eq#SZEH~$zCBp8J{qU&$!+|aJ{-VoK1 zMeDGLjE%M;)nlUt9;hE<16^IldjQBGun?VsW`Z4-z7`u+_9}lM9B7F znenec?ai=A@w~S=2=XAz_xy|QJW~j}-|dw76g%vnMfz}X%;0_-y>8;pERA&jWlQuBcir(1W%jK?QR+aj1vw? zS3K5-1PPe`j^P3cup$sn2}k{JBtL+uH4!ApNdG&&`hh?k5j7~q{u>GeplX>(!h`-Z r_I`edKp@zRf8e0Lj{pjjzy`5^ae3Va0hY%5A52M#%8677=?DBD&C=Cy literal 0 HcmV?d00001 diff --git a/docs/source/images/SNSplit_3.png b/docs/source/images/SNSplit_3.png new file mode 100644 index 0000000000000000000000000000000000000000..14bbceea93eec61e99f81a55d05c74df585b2464 GIT binary patch literal 10714 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 z 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 aRl-=CIi8QNbpF57gGzGhvOlFQzWgs{pfn@^ literal 0 HcmV?d00001 diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 00000000..34488b4e --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,33 @@ +.. Hecuba-read_the_docs documentation master file, created by + sphinx-quickstart on Thu Jan 18 10:23:54 2024. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Hecuba's user manual! +================================================ + +In this manual, we describe how to implement a Python application using `Hecuba `_ and which are the main features that Hecuba implements to boost the performance of an application using Cassandra. Hecuba developers can improve their productivity as Hecuba implements all the necessary code to access the data. Thus, applications can access data as if it was in memory and Hecuba translates this code at runtime to access the underlying storage system. Also, Hecuba implements some optimizations to favor data locality and to reduce the number of interactions with the backing storage and thus, to speedup the accesses to data. + +.. image:: images/HecubaBSCLogo2_1.png + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + applications + deployment + basics + pycompss + execution + exe_pycompss + conf_param + cplusplus + lambda + +.. + Indices and tables + ================== + + * :ref:`genindex` + * :ref:`modindex` + * :ref:`search` diff --git a/docs/source/lambda.rst b/docs/source/lambda.rst new file mode 100644 index 00000000..4ec8d7bb --- /dev/null +++ b/docs/source/lambda.rst @@ -0,0 +1,64 @@ +.. _lamba: + +Lambda architecture in Hecuba +============================= + +Hecuba facilitates the implementation of using both on-line and off-line processing by implementing a lambda architecture. With this architecture, the data is streamed concurrently with the data insertion in storage. + +Currently we have implemented this feature for StorageDicts. The user has to define the class as a subclass of both StorageDict and StorageStream Hecuba classes. With this class definition, all the setitems will send asynchronously the data to the storage and synchronously through the stream. + +Exemple: python specification (to be used from python code) + +.. code-block:: python + + class mydict (StorageDict, StorageStream): + ''' + @TypeSpec dict <,val:numpy.ndarray> + ''' + +Exemple: C++ class definition (to be used from c++ code) + +.. code-block:: cpp + + class myDictClass : public StorageDict, public + StorageStream{ + + } + +The consumer should use the function poll to receive the streamed data. At this point of the implementation, poll is only supported by the Python interface of Hecuba. + +.. code-block:: python + + def poll() # returns the key and the value of the streamed data + +Example: consumer in Python + +.. code-block:: python + + d=mydict("dictname") + k,v=d.poll() #k will value 42 and v will be sn + +The code for the producer is the same than when the streaming capability is not set. + +Example: producer in Python + +.. code-block:: python + + d=mydict("dictname") + d[42]=sn # sn is a StorageNumpy + +Example: producer in C++ + +.. code-block:: cpp + + myDictClass d; + d.make_persistent("streamdict"); + + // add here the declaration of k and v + + d[k] = v; //will store this item in the database + // and will send it through the stream + +Notice that the data is stored in the database as a regular StorageDict, so it is possible to implement applications to perform off-line analysis without activating the stream feature in the class definition. + +A full example can be found in `Hecuba Streaming Examples `_, containing a producer implemented both in Python and C++ and its corresponding consumer in Python. diff --git a/docs/source/pycompss.rst b/docs/source/pycompss.rst new file mode 100644 index 00000000..1d4f2deb --- /dev/null +++ b/docs/source/pycompss.rst @@ -0,0 +1,30 @@ +.. _pycompss: + +Support to automatic parallelization: PyCOMPSs +============================================== + +COMPS Superscalar is a framework which aims to ease the development and execution of applications for distributed infrastructures, such as Clusters, Grids and Clouds. This framework implements several bindings to support applications written in different languages. COMPSs runtime exports an interface to implement some optimizations regarding the data storage that for example can be used to enhance data locality. Here we only introduce the main features implemented by Hecuba to interact with COMPSs and, in particular, with the Python binding (from now on, PyCOMPSs); more Documentation about this can be found in the +`COMPSs Manual `_. + +Tasks in PyCOMPSs +***************** + +PyCOMPSs allow programmers to write sequential code and to indicate, through a decorator, which functions can be executed in parallel. The COMPSs runtime interprets this decorator and executes, transparent to the programmer, all the code necessary to schedule each task on a computing node, to manage dependencies between tasks and to send and to serialize the parameters and the returns of the tasks. + +When input/output parameters of a tasks are persistent objects (i.e. their classes implement the Storage API defined to interact with PyCOMPSs), the runtime asks the storage system for the data locality information and uses this information to try to schedule the task on the node containing the data. This way no data sending or serialization is needed. + +The following code shows an example of PyCOMPSs task. The input parameter of the task could be an object resulting from splitting a StorageDict. In this example the return of the task is a Python dictionary. + +.. code-block:: python + + @task(returns = dict) + def wordcountTask(partition): + partialResult = {} + for word in partition.values(): + if word not in partialResult: + partialResult[word] = 1 + else: + partialResult[word] = partialResult[word] + 1 + return partialResult + +.. image:: images/DataLocality_4.png diff --git a/requirements.txt b/requirements.txt index f266f27b..1b8704ad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,6 @@ numpy >= 1.16 nose>=1.3.7 mock ccm +##sphinx-read_the_docs +bluepy +sphinx_rtd_theme