I am not understanding the use of pool with std::set. i.e. the release_memory() function referenced in last example shown on: http://www.boost.org/doc/libs/1_42_0/libs/pool/doc/interfaces.html seems to do nothing. I call GetProcessMemoryInfo() and don't see any change after the set has gone out of scope. maybe someone could relate what happens to the allocated memory when set goes out of scope and release_memory() isn't called versus what happens when set goes out of scope and release_memory() is called. I want to do something like: 1.std::set<unsigned int, std::less<unsigned int>, boost::pool_allocator<unsigned int>> v; 2. for(int i=0; i<(K); i++) { v.insert(i); } 3. use v 4. release_memory() on v //dump all set elements in one action. Note, without custom allocator this is very slow for large sets. 5. repeat 1-4 with a new set
AMDG B Hart wrote:
I am not understanding the use of pool with std::set. i.e. the release_memory() function referenced in last example shown on: http://www.boost.org/doc/libs/1_42_0/libs/pool/doc/interfaces.html seems to do nothing.
I call GetProcessMemoryInfo() and don't see any change after the set has gone out of scope.
maybe someone could relate what happens to the allocated memory when set goes out of scope and release_memory() isn't called versus what happens when set goes out of scope and release_memory() is called.
I want to do something like:
1.std::set<unsigned int, std::less<unsigned int>, boost::pool_allocator<unsigned int>> v; 2. for(int i=0; i<(K); i++) { v.insert(i);
} 3. use v 4. release_memory() on v //dump all set elements in one action. Note, without custom allocator this is very slow for large sets. 5. repeat 1-4 with a new set
std::set rebinds the allocator to its internal node type. Thus, the allocator actually used by the set is using a different underlying pool than v. In Christ, Steven Watanabe
Sorry, I don't get you...as I understand the pool is a singleton, and once out of scope I would assume all the memory for the set is released back to the OS. Therefore what is the purpose of release_memory()...maybe I don't understand pool. And then "internal node type"...what is that? On Wed, Mar 17, 2010 at 11:21 AM, Steven Watanabe <watanabesj@gmail.com> wrote:
AMDG
B Hart wrote:
I am not understanding the use of pool with std::set. i.e. the release_memory() function referenced in last example shown on: http://www.boost.org/doc/libs/1_42_0/libs/pool/doc/interfaces.html seems to do nothing.
I call GetProcessMemoryInfo() and don't see any change after the set has gone out of scope.
maybe someone could relate what happens to the allocated memory when set goes out of scope and release_memory() isn't called versus what happens when set goes out of scope and release_memory() is called.
I want to do something like:
1.std::set<unsigned int, std::less<unsigned int>, boost::pool_allocator<unsigned int>> v; 2. for(int i=0; i<(K); i++) { v.insert(i);
} 3. use v 4. release_memory() on v //dump all set elements in one action. Note, without custom allocator this is very slow for large sets. 5. repeat 1-4 with a new set
std::set rebinds the allocator to its internal node type. Thus, the allocator actually used by the set is using a different underlying pool than v.
In Christ, Steven Watanabe
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
B Hart wrote:
Sorry, I don't get you...as I understand the pool is a singleton, and once out of scope I would assume all the memory for the set is released back to the OS.
If you mean when the std::set goes out of scope, then no.
And then "internal node type"...what is that?
Some STL containers (std::list, std::set, etc.) don't directly store elements of the type you specify. For example, a std::list would likely store list nodes that contain the data element itself as well as pointers to the previous and next nodes. In that case, a std::list< Foo, boost::pool_allocator<Foo> > doesn't allocate via boost::singleton_pool<boost::pool_allocator_tag, sizeof(Foo)>. It allocates via boost::singleton_pool<boost::pool_allocator_tag, sizeof(SomeUnknownInternalNodeType)>.
I'm understanding that it is not allocating unsigned ints and instead creating a node structure wherein uint is a subelement. The nodes are needed because set is internally kept as a tree structure. What I'm not understanding is when is the memory released? and when is the instance of the set destroyed? On Wed, Mar 17, 2010 at 1:27 PM, Kenny Riddile <kfriddile@yahoo.com> wrote:
B Hart wrote:
Sorry, I don't get you...as I understand the pool is a singleton, and once out of scope I would assume all the memory for the set is released back to the OS.
If you mean when the std::set goes out of scope, then no.
And then "internal node type"...what is that?
Some STL containers (std::list, std::set, etc.) don't directly store elements of the type you specify. For example, a std::list would likely store list nodes that contain the data element itself as well as pointers to the previous and next nodes. In that case, a std::list< Foo, boost::pool_allocator<Foo> > doesn't allocate via boost::singleton_pool<boost::pool_allocator_tag, sizeof(Foo)>. It allocates via boost::singleton_pool<boost::pool_allocator_tag, sizeof(SomeUnknownInternalNodeType)>.
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
AMDG B Hart wrote:
Sorry, I don't get you...as I understand the pool is a singleton, and once out of scope I would assume all the memory for the set is released back to the OS. Therefore what is the purpose of release_memory()...maybe I don't understand pool. And then "internal node type"...what is that?
Set is implemented using a binary search tree. set<int> doesn't actually allocate ints, it allocates a struct that probably looks something like: struct Node { int value; int color; Node * left; Node * right; Nonde * parent; }; pool_allocator uses separate singleton pools for each size of element that it needs to handle. In Christ, Steven Watanabe
Ok understood, but ?s: 1. when the set goes out of scope does the instance of the class get destroyed? As I understand, singleton only guarantees that there is one instance, so the implementation semantics could vary. e.g. the singleton is more like a class factory guaranteeing 1 instance. 2. When the set goes out of scope is any memory released? Setting the node to it's internal node type sort of implies that the memory has been released already. 3. "the allocator actually used by the set is using a different underlying pool than v"...what do you mean??? there are two pools. I'm getting confused. On Wed, Mar 17, 2010 at 1:27 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
AMDG
B Hart wrote:
Sorry, I don't get you...as I understand the pool is a singleton, and once out of scope I would assume all the memory for the set is released back to the OS. Therefore what is the purpose of release_memory()...maybe I don't understand pool. And then "internal node type"...what is that?
Set is implemented using a binary search tree. set<int> doesn't actually allocate ints, it allocates a struct that probably looks something like:
struct Node { int value; int color; Node * left; Node * right; Nonde * parent; };
pool_allocator uses separate singleton pools for each size of element that it needs to handle.
In Christ, Steven Watanabe
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
AMDG B Hart wrote:
1. when the set goes out of scope does the instance of the class get destroyed? As I understand, singleton only guarantees that there is one instance, so the implementation semantics could vary. e.g. the singleton is more like a class factory guaranteeing 1 instance. 2. When the set goes out of scope is any memory released? Setting the node to it's internal node type sort of implies that the memory has been released already.
The memory is released back to the pool, but not to the system, when the set is destroyed.
3. "the allocator actually used by the set is using a different underlying pool than v"...what do you mean??? there are two pools. I'm getting confused.
There is one pool for each size of object. In Christ, Steven Watanabe
Sorry, if this is a little late to be helpful but I thought I'd add this for historical purposes for anyone looking at this thread to deal with their own problem. All STL containers use allocators to allocate and initialize the memory to store the _elements_ in the container not the node structure. The containers definition of the data structures that support the implentation details are purely internal. The allocator interface is paramterized on the type of the container element.
B Hart wrote:
Sorry, I don't get you...as I understand the pool is a singleton, and once out of scope I would assume all the memory for the set is released back to the OS. Therefore what is the purpose of release_memory()...maybe I don't understand pool. And then "internal node type"...what is that?
Set is implemented using a binary search tree. set<int> doesn't actually allocate ints, it allocates a struct that probably looks something like:
struct Node { int value; int color; Node * left; Node * right; Nonde * parent; };
pool_allocator uses separate singleton pools for each size of element that it needs to handle.
In Christ, Steven Watanabe
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
Sorry, if this is a little late to be helpful but I thought I'd add this for historical purposes for anyone looking at this thread to deal with their own problem. All STL containers use allocators to allocate and initialize the memory to store the _elements_ in the container not the node structure. The containers definition of the data structures that support the implentation details are purely internal. The allocator interface is paramterized on the type of the container element.
In fact what I've done once is to have both the set object and the nodes allocated in a kind of pool, and then just not call the destructor (just have the set and its nodes disappear with the pool/pools). THAT is a speed-up (otherwise a set gets years to destroy). Filip
B Hart wrote:
Sorry, I don't get you...as I understand the pool is a singleton, and once out of scope I would assume all the memory for the set is released back to the OS. Therefore what is the purpose of release_memory()...maybe I don't understand pool. And then "internal node type"...what is that?
Set is implemented using a binary search tree. set<int> doesn't actually allocate ints, it allocates a struct that probably looks something like:
struct Node { int value; int color; Node * left; Node * right; Nonde * parent; };
pool_allocator uses separate singleton pools for each size of element that it needs to handle.
In Christ, Steven Watanabe
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
So.. have you modified STL code? Or is boost::pool some how able to reimplement std::set (or any STL container) node alllocation code? That would be quite a trick because, all else being equal, if all you are doing is providing your own allocator to an STL container you will not change how the container allocates it's internal implementation data structrures. You will only be changing how elements are allocated. Eric ----- Original Message ----- From: "Filip Konvicka" <filip.konvicka@logis.cz> To: <boost-users@lists.boost.org> Sent: Friday, March 19, 2010 2:09 PM Subject: Re: [Boost-users] custom allocators Re:pool_alloc
Sorry, if this is a little late to be helpful but I thought I'd add this for historical purposes for anyone looking at this thread to deal with their own problem. All STL containers use allocators to allocate and initialize the memory to store the _elements_ in the container not the node structure. The containers definition of the data structures that support the implentation details are purely internal. The allocator interface is paramterized on the type of the container element.
In fact what I've done once is to have both the set object and the nodes allocated in a kind of pool, and then just not call the destructor (just have the set and its nodes disappear with the pool/pools). THAT is a speed-up (otherwise a set gets years to destroy).
Filip
B Hart wrote:
Sorry, I don't get you...as I understand the pool is a singleton, and once out of scope I would assume all the memory for the set is released back to the OS. Therefore what is the purpose of release_memory()...maybe I don't understand pool. And then "internal node type"...what is that?
Set is implemented using a binary search tree. set<int> doesn't actually allocate ints, it allocates a struct that probably looks something like:
struct Node { int value; int color; Node * left; Node * right; Nonde * parent; };
pool_allocator uses separate singleton pools for each size of element that it needs to handle.
In Christ, Steven Watanabe
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
On 19.3.2010 20:22, Eric Whitcombe wrote:
So.. have you modified STL code? Or is boost::pool some how able to reimplement std::set (or any STL container) node alllocation code? That would be quite a trick because, all else being equal, if all you are doing is providing your own allocator to an STL container you will not change how the container allocates it's internal implementation data structrures. You will only be changing how elements are allocated.
Please don't top-post. Of course I did not modify the STL. As Steven is pointing out, containers use rebind to get the allocators they need. Usually, the node contains the user's type plus some node information (like a pointer or two, or color etc.). What I did is better done with a non-singleton allocator that consumes memory of one or more local 'pools'. You allocate the container itself within this memory (using placement new or something similar), and you provide the container with an allocator that also takes memory from there. Then you just leave the scope, the pool(s) disappears and the container plus all the data just disappears with them, no destructors called. So this is only safe if you know what you are doing - you should only store ints or something that does not need their destructors called (for example in order to prevent memory leaks, which you would get with strings). I guess this is in fact what the OP wishes to do (he's surprised that the singleton pool does not sometimes release memory to the system, which is in fact the expected behavior). For reference, this is the message on the comp.lib.gecode.user newsgroup where I posted the code: http://article.gmane.org/gmane.comp.lib.gecode.user/2187/match=allocator Actually I think it's part of Gecode distribution now (see www.gecode.org). Also see http://www.gecode.org/doc-latest/reference/group__FuncMemAllocator.html HTH, Filip
----- Original Message ----- From: "Filip Konvicka" <filip.konvicka@logis.cz> To: <boost-users@lists.boost.org> Sent: Friday, March 19, 2010 2:09 PM Subject: Re: [Boost-users] custom allocators Re:pool_alloc
Sorry, if this is a little late to be helpful but I thought I'd add this for historical purposes for anyone looking at this thread to deal with their own problem. All STL containers use allocators to allocate and initialize the memory to store the _elements_ in the container not the node structure. The containers definition of the data structures that support the implentation details are purely internal. The allocator interface is paramterized on the type of the container element.
In fact what I've done once is to have both the set object and the nodes allocated in a kind of pool, and then just not call the destructor (just have the set and its nodes disappear with the pool/pools). THAT is a speed-up (otherwise a set gets years to destroy).
Filip
B Hart wrote:
Sorry, I don't get you...as I understand the pool is a singleton, and once out of scope I would assume all the memory for the set is released back to the OS. Therefore what is the purpose of release_memory()...maybe I don't understand pool. And then "internal node type"...what is that?
Set is implemented using a binary search tree. set<int> doesn't actually allocate ints, it allocates a struct that probably looks something like:
struct Node { int value; int color; Node * left; Node * right; Nonde * parent; };
pool_allocator uses separate singleton pools for each size of element that it needs to handle.
In Christ, Steven Watanabe
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
On 19 March 2010 14:02, Eric Whitcombe <ericwsf@gmail.com> wrote:
Sorry, if this is a little late to be helpful but I thought I'd add this for historical purposes for anyone looking at this thread to deal with their own problem. All STL containers use allocators to allocate and initialize the memory to store the _elements_ in the container not the node structure. The containers definition of the data structures that support the implentation details are purely internal. The allocator interface is paramterized on the type of the container element.
On that note, does anyone know why allocators are parametrised at all? Since std::vector is the only container that actually allocates from passed-in allocator, it seems like it would have been simpler to just use member templates in a non-templated allocator class. (Parametrised allocators also mean that nested containers have exponential space requirements for their names, which actually crashed my compiler once when I tried to make a deeply-nested container structure in some recursive-slowdown experiments.)
Allocators are conceptually indepent from the containers that use them. They can be useful anywhere memory has to be allocated. They have to be parameterized so you allocate the correct size and invoke the correct copy constructor. All the STL containers allow you to use the default allocator and the argument supplied to the allocator template parameter is the element parameter supplied. to the container. That's why you are only required to specifiy the element type when declaring most container class instantiations. Like std::vector<int> MyIntVector; I don't know what you mean when you say "vector is the only container that actually allocates from passed-in allocator." All containers allocate memory for a copy of the element you add. That is why one of the requirements for user-defined types to be stored in STL containers is that they have to be copy-constructible.
it seems like it would have been simpler to just use member templates in a non-templated allocator class.
I don't see how much simpler that would be but I may not be understanding what you mean. The interface for allocators specified by the STL is actually pretty simple.
Parametrised allocators also mean that nested containers have exponential space requirements for their names, which actually crashed my compiler once when I tried to make a deeply-nested container structure in some recursive-slowdown experiments.)
I'd say that has more to do with your compiler and the general hazards of recursion. There is no infinite capacity for recursion whether it be your compiler's symbol table capacty (or it's inability to simplify the symbol table) or the limits on stack space at run time. ----- Original Message ----- From: "Scott McMurray" <me22.ca+boost@gmail.com> To: <boost-users@lists.boost.org> Sent: Friday, March 19, 2010 2:14 PM Subject: Re: [Boost-users] custom allocators Re:pool_alloc
On 19 March 2010 14:02, Eric Whitcombe <ericwsf@gmail.com> wrote:
Sorry, if this is a little late to be helpful but I thought I'd add this for historical purposes for anyone looking at this thread to deal with their own problem. All STL containers use allocators to allocate and initialize the memory to store the _elements_ in the container not the node structure. The containers definition of the data structures that support the implentation details are purely internal. The allocator interface is paramterized on the type of the container element.
On that note, does anyone know why allocators are parametrised at all?
Since std::vector is the only container that actually allocates from passed-in allocator, it seems like it would have been simpler to just use member templates in a non-templated allocator class.
(Parametrised allocators also mean that nested containers have exponential space requirements for their names, which actually crashed my compiler once when I tried to make a deeply-nested container structure in some recursive-slowdown experiments.) _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
On 19 March 2010 15:12, Eric Whitcombe <ericwsf@gmail.com> wrote:
I don't know what you mean when you say "vector is the only container that actually allocates from passed-in allocator." All containers allocate memory for a copy of the element you add. That is why one of the requirements for user-defined types to be stored in STL containers is that they have to be copy-constructible.
I mean that all the other containers need to rebind the allocator, so for consistency, it seems like vector should too. But then once everything is rebinding the allocator, it no longer makes sense to parametrise the passed-in allocator by the type, since it's just going to rebind it. Basically, the most basic way would just be to do: template <typename T = void> class allocator { ... as currently ... }; template <> class allocator<void> { template <typename T> struct rebind { typedef allocator<T> type; }; }; Then you would just pass std::allocator<> to all the containers, and everything could easily be made to work.
I'd say that has more to do with your compiler and the general hazards of recursion. There is no infinite capacity for recursion whether it be your compiler's symbol table capacty (or it's inability to simplify the symbol table) or the limits on stack space at run time.
The point is that (using A for std::allocator and V for std::vector), a V<V<V<T>>> is actually a V<V<V<T,A<T>>, A<V<T,A<T>>>>,A<V<V<T,A<T>>, A<V<T,A<T>>>>>> because the names are exponential in depth, rather than linear in depth as they ought to be. With a non-parametrised allocator, it'd just be V<V<V<T,A>,A>,A> which is much easier to deal with.
Yeah, I see what I am missing - Steve pointed it out that _node-based_ conatiners use the same allocator with rebind. Exponential growth - there oughtta be a law. ;-) ----- Original Message ----- From: "Scott McMurray" <me22.ca+boost@gmail.com> To: <boost-users@lists.boost.org> Sent: Friday, March 19, 2010 3:50 PM Subject: Re: [Boost-users] custom allocators Re:pool_alloc
On 19 March 2010 15:12, Eric Whitcombe <ericwsf@gmail.com> wrote:
I don't know what you mean when you say "vector is the only container that actually allocates from passed-in allocator." All containers allocate memory for a copy of the element you add. That is why one of the requirements for user-defined types to be stored in STL containers is that they have to be copy-constructible.
I mean that all the other containers need to rebind the allocator, so for consistency, it seems like vector should too. But then once everything is rebinding the allocator, it no longer makes sense to parametrise the passed-in allocator by the type, since it's just going to rebind it.
Basically, the most basic way would just be to do:
template <typename T = void> class allocator { ... as currently ... };
template <> class allocator<void> { template <typename T> struct rebind { typedef allocator<T> type; }; };
Then you would just pass std::allocator<> to all the containers, and everything could easily be made to work.
I'd say that has more to do with your compiler and the general hazards of recursion. There is no infinite capacity for recursion whether it be your compiler's symbol table capacty (or it's inability to simplify the symbol table) or the limits on stack space at run time.
The point is that (using A for std::allocator and V for std::vector), a
V<V<V<T>>>
is actually a
V<V<V<T,A<T>>, A<V<T,A<T>>>>,A<V<V<T,A<T>>, A<V<T,A<T>>>>>>
because the names are exponential in depth, rather than linear in depth as they ought to be.
With a non-parametrised allocator, it'd just be
V<V<V<T,A>,A>,A>
which is much easier to deal with. _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
AMDG Scott McMurray wrote:
On 19 March 2010 14:02, Eric Whitcombe <ericwsf@gmail.com> wrote:
Sorry, if this is a little late to be helpful but I thought I'd add this for historical purposes for anyone looking at this thread to deal with their own problem. All STL containers use allocators to allocate and initialize the memory to store the _elements_ in the container not the node structure. The containers definition of the data structures that support the implentation details are purely internal. The allocator interface is paramterized on the type of the container element.
On that note, does anyone know why allocators are parametrised at all?
Probably because the original idea was just to allocate the type passed. Then someone realized that doesn't work for node-based containers and said, ah, we'll just add rebind.
Since std::vector is the only container that actually allocates from passed-in allocator, it seems like it would have been simpler to just use member templates in a non-templated allocator class.
Well, you still need the pointer, reference, etc. typedefs, somehow.
(Parametrised allocators also mean that nested containers have exponential space requirements for their names, which actually crashed my compiler once when I tried to make a deeply-nested container structure in some recursive-slowdown experiments.)
Yep. Probably no one thought ot this until it too late to do anything about it. In Christ, Steven Watanabe
AMDG Eric Whitcombe wrote:
Sorry, if this is a little late to be helpful but I thought I'd add this for historical purposes for anyone looking at this thread to deal with their own problem. All STL containers use allocators to allocate and initialize the memory to store the _elements_ in the container not the node structure.
This is not true.
The containers definition of the data structures that support the implentation details are purely internal. The allocator interface is paramterized on the type of the container element.
All node based containers use rebind to get an allocator for the internal node type. In Christ, Steven Watanabe
I'm sorry but you would have to point me to some sources where you are getting your info. I can point you to Josuttis "The C++ Standard Library: A Tutorial and Reference" p 734. In part about rebind: "This template structure provides the ability that an allocator may allocate storage of another type indirectly." This page gives detailed example vis-a-vis std::deque. I won't quote it at length. In short rebind allows you to abstract the implementation of allocating the internal data structures and link it to the allocation of dynamic type of the element. ----- Original Message ----- From: "Steven Watanabe" <watanabesj@gmail.com> To: <boost-users@lists.boost.org> Sent: Friday, March 19, 2010 2:54 PM Subject: Re: [Boost-users] custom allocators Re:pool_alloc
AMDG
Eric Whitcombe wrote:
Sorry, if this is a little late to be helpful but I thought I'd add this for historical purposes for anyone looking at this thread to deal with their own problem. All STL containers use allocators to allocate and initialize the memory to store the _elements_ in the container not the node structure.
This is not true.
The containers definition of the data structures that support the implentation details are purely internal. The allocator interface is paramterized on the type of the container element.
All node based containers use rebind to get an allocator for the internal node type.
In Christ, Steven Watanabe
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
AMDG Eric Whitcombe wrote:
I'm sorry but you would have to point me to some sources where you are getting your info. I can point you to Josuttis "The C++ Standard Library: A Tutorial and Reference" p 734.
Look at any implementation of the standard library. Here's the struct that's actually allocated in one: struct _Node { // tree node <snip constructor> <snip tree links> value_type _Myval; // the stored value, unused if head <snip extra bookkeeping information> }; Note that because the node holds the object by value, there is no way to separate allocation of the node from allocation of the value.
In part about rebind: "This template structure provides the ability that an allocator may allocate storage of another type indirectly." This page gives detailed example vis-a-vis std::deque. I won't quote it at length.
I don't see how this leads to the conclusion that the container must allocate exactly the type you give it. Maybe I'm not understanding you correctly?
In short rebind allows you to abstract the implementation of allocating the internal data structures and link it to the allocation of dynamic type of the element.
The standard actually does not specify what is actually allocated. The allocator is just along for the ride for all the containers, and it's assumed that anything that the container needs to allocate will use either the allocator or a rebound form of the allocator. In Christ, Steven Watanabe
participants (6)
-
B Hart
-
Eric Whitcombe
-
Filip Konvička
-
Kenny Riddile
-
Scott McMurray
-
Steven Watanabe