Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
solverstack
ScalFMM
Commits
d5ac184f
Commit
d5ac184f
authored
Aug 08, 2016
by
Quentin Khan
Browse files
Adaptive tree: group memory allocation
parent
bb75ec64
Changes
4
Hide whitespace changes
Inline
Side-by-side
Src/Adaptive/new/FNode.hpp
View file @
d5ac184f
...
...
@@ -691,12 +691,11 @@ private:
/** Allocates this node's children */
void
create_children
()
{
using
uninit_FNode
=
typename
std
::
aligned_storage
<
sizeof
(
FNode
)
>::
type
;
std
::
size_t
idx
=
0
;
// Remove this node from tree leaf list
getTree
().
leaves
().
erase
(
this
);
// Create the children, add them to tree leaf list
FNode
*
tmp
=
reinterpret_cast
<
FNode
*>
(
new
uninit_FNode
[
child_count
]
);
FNode
*
tmp
=
this
->
getTree
().
node_memory_manager
.
provide
(
this
->
getDepth
()
+
1
,
child_count
);
for
(
FNode
*&
child
:
getChildren
())
{
child
=
new
(
tmp
+
idx
)
FNode
(
*
this
,
idx
);
getTree
().
leaves
().
insert
(
child
);
...
...
@@ -710,7 +709,6 @@ private:
/* Deletes this node's children */
void
delete_children
()
{
using
uninit_FNode
=
typename
std
::
aligned_storage
<
sizeof
(
FNode
)
>::
type
;
// Remove children from tree leaf list, free them
for
(
FNode
*&
child
:
getChildren
())
{
if
(
child
)
{
...
...
@@ -718,7 +716,6 @@ private:
child
->~
FNode
();
}
}
delete
[]
reinterpret_cast
<
uninit_FNode
*>
(
getChild
(
0
));
std
::
fill_n
(
this
->
getChildren
().
data
(),
child_count
,
nullptr
);
// Insert this node in tree leaf list
getTree
().
leaves
().
insert
(
this
);
...
...
Src/Adaptive/new/FTree.hpp
View file @
d5ac184f
...
...
@@ -9,6 +9,7 @@
#include
"FNodeIteratorBox.hpp"
#include
"FInOrderNodeIterator.hpp"
#include
"FPrePostOrderNodeIterator.hpp"
#include
"UninitNodeMemoryManager.hpp"
/** \brief Adaptive FMM tree
*
...
...
@@ -72,10 +73,13 @@ private:
/// Tree height
std
::
size_t
_height
=
0
;
UninitNodeMemoryManager
<
node_t
>
node_memory_manager
;
public:
/** \brief Swaps two trees */
void
swap
(
FTree
&
tree
)
{
using
std
::
swap
;
swap
(
node_memory_manager
,
tree
.
node_memory_manager
);
swap
(
_max_height
,
tree
.
_max_height
);
swap
(
_box
,
tree
.
_box
);
swap
(
_root
,
tree
.
_root
);
...
...
@@ -96,7 +100,8 @@ public:
*
*/
FTree
(
box_t
box_
)
:
_box
(
box_
)
{
_root
=
new
node_t
(
this
);
_root
=
this
->
node_memory_manager
.
provide
(
0
,
1
);
new
(
this
->
_root
)
node_t
(
this
);
}
FTree
(
FTree
&
)
=
delete
;
...
...
@@ -115,7 +120,7 @@ public:
/** \brief Destructor */
~
FTree
()
{
delete
_root
;
_root
->~
node_t
()
;
}
/** \brief Tree height
...
...
Src/Adaptive/new/UninitNodeMemoryManager.hpp
0 → 100644
View file @
d5ac184f
#ifndef UNINITNODEMEMORYMANAGER_HPP
#define UNINITNODEMEMORYMANAGER_HPP
#include
<functional>
#include
<list>
#include
<vector>
#include
"UninitialisedMemoryProvider.hpp"
/**
* \brief Manages uninitialised memory for nodes
*
* This class' intent is to provide block memory management for nodes. This
* reduces the amount of memory allocations.
*
* The nodes' memory is handled per level. Memory for a level is not garanteed
* to be contiguous, it is however contiguous by block.
*
* The memory requests are forwarded to UninitialisedMemoryProvider objects. A
* vector of list of providers is kept to ensure constant access to a level. The
* lists allow constant access to their last provider and allow adding new ones
* without reallocating the entire list.
*
* Memory is alway requested from the last provider in a list. When the last
* provider does not have enough memory available, a new one is appended to the
* list.
*
* \tparam _Node Node type
*/
template
<
class
_Node
>
class
UninitNodeMemoryManager
{
/**
* \brief Policy for memory provider capacity
*
* \param level Level of the nodes in the tree
* \param count Node count
*
* \return The provider size
*/
std
::
function
<
std
::
size_t
(
std
::
size_t
level
,
std
::
size_t
count
)
>
provider_size_policy
=
[](
const
std
::
size_t
level
,
const
std
::
size_t
count
)
{
enum
{
max_level
=
4
};
return
std
::
max
(
std
::
size_t
(
1
)
<<
3
*
(
level
>
max_level
?
max_level
:
level
),
count
);
};
/**
* \brief memory provider lists
*
* The vector allows constant access to any level, the lists allow constant
* access to the last element.
*/
std
::
vector
<
std
::
list
<
UninitialisedMemoryProvider
<
_Node
>>>
providers
;
/**
* \brief Fill the providers vector until given level is reached
*
* \param level Tree level
*/
void
add_until_level
(
const
std
::
size_t
level
)
noexcept
{
const
std
::
size_t
provider_count
=
this
->
providers
.
size
();
if
(
level
<
provider_count
)
{
return
;
}
this
->
providers
.
resize
(
level
+
1
);
for
(
std
::
size_t
l
=
provider_count
;
l
<
level
+
1
;
++
l
)
{
this
->
providers
[
l
].
emplace_back
(
provider_size_policy
(
level
,
0
));
}
}
public:
/**
* \brief Provide uninitialised memory for given node count and tree level
*
* \param level Node level in the tree
* \param count Node count
*
* \return A pointer to the first node. Memory is uninitialised.
*/
_Node
*
provide
(
const
std
::
size_t
level
,
const
std
::
size_t
count
)
noexcept
{
// Check that a provider for given level exists
if
(
this
->
providers
.
size
()
<
level
+
1
)
{
this
->
add_until_level
(
level
+
1
);
}
// Add a provider when needed
if
(
!
this
->
providers
[
level
].
back
().
can_provide
(
count
))
{
this
->
providers
[
level
].
emplace_back
(
provider_size_policy
(
level
,
count
));
}
return
this
->
providers
[
level
].
back
().
provide
(
count
);
}
};
#endif
/* UNINITNODEMEMORYMANAGER_HPP */
Src/Adaptive/new/UninitialisedMemoryProvider.hpp
0 → 100644
View file @
d5ac184f
#ifndef UNINITIALISEDMEMORYPROVIDER_HPP
#define UNINITIALISEDMEMORYPROVIDER_HPP
#include
<cmath>
#include
<memory>
#include
<sstream>
/**
* \brief Basic item provider
*
* A basic uninitialised memory provider. Provides heap allocation of a constant
* size array of objects. No reallocation/release can happen apart from deleting
* the provider.
*
* \tparam T Type of the object to provide memory for
*/
template
<
class
T
>
class
UninitialisedMemoryProvider
{
public:
using
type
=
T
;
private:
using
uninit_item
=
typename
std
::
aligned_storage
<
sizeof
(
T
)
>::
type
;
const
std
::
size_t
_capacity
=
1
<<
3
;
std
::
size_t
_size
=
0
;
std
::
unique_ptr
<
uninit_item
[]
>
_data
=
std
::
unique_ptr
<
uninit_item
[]
>
{
new
uninit_item
[
this
->
_capacity
]};
std
::
size_t
&
size
()
noexcept
{
return
this
->
_size
;
}
/**
* \brief Accessor to the underlying data
*/
type
*
data
()
noexcept
{
return
reinterpret_cast
<
type
*>
(
this
->
_data
.
get
());
}
public:
UninitialisedMemoryProvider
()
=
delete
;
UninitialisedMemoryProvider
(
const
UninitialisedMemoryProvider
&
)
=
delete
;
UninitialisedMemoryProvider
&
operator
=
(
const
UninitialisedMemoryProvider
&
)
=
delete
;
UninitialisedMemoryProvider
(
UninitialisedMemoryProvider
&&
)
=
default
;
UninitialisedMemoryProvider
&
operator
=
(
UninitialisedMemoryProvider
&&
)
=
default
;
/**
* \brief Create a provider that may contain count items
*
* \param count Bucket capacity
*/
UninitialisedMemoryProvider
(
std
::
size_t
count
)
:
_capacity
(
count
)
{}
/**
* \brief Total capacity of the provider
*/
std
::
size_t
capacity
()
const
noexcept
{
return
this
->
_capacity
;
}
/**
* \brief Already provided object count
*/
std
::
size_t
size
()
const
noexcept
{
return
this
->
_size
;
}
/**
* \brief Read-only accessor to the underlying data
*/
const
type
*
data
()
const
noexcept
{
return
reinterpret_cast
<
type
*>
(
this
->
_data
.
get
());
}
/**
* \brief Provide memory for given object count
*
* Checks that the provider can issue enough memory for the required object
* count and returns a pointer to the first one.
*
* \param count Required object count
*
* \return An array of uninitialised objects
*
* \exception std::length_error When the requested count cannot be provided
* (capacity - size < count)
*/
type
*
provide
(
const
std
::
size_t
count
)
{
if
(
!
this
->
can_provide
(
count
))
{
std
::
stringstream
sstr
;
sstr
<<
"Cannot provide enough items, asked for "
;
sstr
<<
count
<<
", got "
<<
this
->
capacity
()
-
this
->
size
();
throw
std
::
length_error
(
sstr
.
str
());
}
return
unsafe_provide
(
count
);
}
/**
* \brief Checks whether the provider has enought memory left or not
*
* \param count Object count
*
* \return True if at least memory for count items is available, false
* otherwise.
*/
bool
can_provide
(
const
std
::
size_t
count
)
const
noexcept
{
return
this
->
size
()
+
count
<=
this
->
capacity
();
}
/**
* \brief Provide memory for given object count without checks
*
* This version of the #provide method does not check nor throw in case or
* error, it is up to the user to ensure that enough memory is available
* before calling it.
*
* The provider size is updated accordinly to the request, therefore it is
* possible to check after the call is size <= capacity.
*
* \param count The required object count
*
* \return An array of uninitialised objects
*/
type
*
unsafe_provide
(
const
std
::
size_t
count
)
noexcept
{
auto
ret
=
this
->
data
()
+
this
->
size
();
this
->
_size
+=
count
;
return
ret
;
}
};
#endif
/* UNINITIALISEDMEMORYPROVIDER_HPP */
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment