Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Ready] Fast Concurrent Deque Through Explicit Timestamping #127

Open
wants to merge 30 commits into
base: integration
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
9d2f2af
Init ts_deque
pr3sto Dec 4, 2018
4e31819
init deque_buffer
EduardBlees Dec 6, 2018
fff2b21
Merge branch 'develop' of github.com:EduardBlees/libcds into develop
EduardBlees Dec 6, 2018
c20d3c3
init timestamp
vikkiorrikki Dec 6, 2018
86ff17d
add Item and initialize method
pr3sto Dec 8, 2018
c7c96bf
try remove left impl
EduardBlees Dec 10, 2018
9f6d53e
threadcontext
EduardBlees Dec 10, 2018
92efc84
implement insert_right
pr3sto Dec 12, 2018
a136c71
timestamp hardware
vikkiorrikki Dec 12, 2018
0dcef53
insert_left implementation
pr3sto Dec 13, 2018
7fdf05c
try remove right impl
EduardBlees Dec 15, 2018
ab723b5
timestamp hardware interval
vikkiorrikki Dec 15, 2018
b2a8b91
implement ts_deque
pr3sto Dec 16, 2018
0178ecb
methods for fixing aba problem added
pr3sto Dec 16, 2018
2bb8dce
try remove fixes, use ABA functions
EduardBlees Dec 17, 2018
06542ae
fixes insert methods
pr3sto Dec 18, 2018
09052d7
timestamp atomic counter
vikkiorrikki Dec 19, 2018
546b8b3
assign threadcontext to each thread
pr3sto Dec 19, 2018
0e4fd30
code format
EduardBlees Dec 20, 2018
0181934
add clear, size and empty methods
pr3sto Dec 23, 2018
c4ecac4
prepare to pull request
pr3sto Dec 24, 2018
eb5e9f6
add unit tests
pr3sto Dec 24, 2018
a5ecc16
move asm code to compiler folder
pr3sto Dec 24, 2018
72f5ef0
fix
pr3sto Dec 24, 2018
ac02558
add copyright note
pr3sto Dec 25, 2018
430297f
fix emptyness check, thread_id
pr3sto Dec 27, 2018
ff40ba7
stress tests added
pr3sto Dec 28, 2018
5ee65b5
fix tests for different architectures
pr3sto Jan 16, 2019
d5ece4c
fix rdtsc and rdtscp for i386 arch
pr3sto Jan 16, 2019
2410e88
add rdtscp check
pr3sto Jan 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions cds/compiler/gcc/amd64/ts_hardwaretimestamp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#ifndef CDSLIB_COMPILER_GCC_AMD64_TS_HARDWARETIMESTAMP_H
#define CDSLIB_COMPILER_GCC_AMD64_TS_HARDWARETIMESTAMP_H

#include <cstdint>
#include <cpuid.h>

namespace cds { namespace tshardwaretimestamp {
namespace gcc { namespace amd64 {

# define CDS_ts_hardwaretimestamp_hwptime_defined
static inline uint64_t get_hwptime()
{
uint64_t aux;
uint64_t rax, rdx;
__asm__ volatile ("rdtscp\n" : "=a" (rax), "=d" (rdx), "=c" (aux) : : );
return (rdx << 32) + rax;
}

# define CDS_ts_hardwaretimestamp_hwtime_defined
static inline uint64_t get_hwtime()
{
uint64_t high, low;
__asm__ volatile("rdtsc" : "=a"(low), "=d"(high));
return ((uint64_t)low) | (((uint64_t)high) << 32);
}

static inline int has_rdtscp()
{
unsigned int eax, ebx, ecx, edx;
if (__get_cpuid(0x80000001, &eax, &ebx, &ecx, &edx))
return (edx >> 27) & 0x1;
else
return 0;
}

}} // namespace gcc::amd64

namespace platform {
using namespace gcc::amd64;
}
}} // namespace cds::tshardwaretimestamp

#endif // #ifndef CDSLIB_COMPILER_GCC_AMD64_TS_HARDWARETIMESTAMP_H
42 changes: 42 additions & 0 deletions cds/compiler/gcc/x86/ts_hardwaretimestamp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#ifndef CDSLIB_COMPILER_GCC_X86_TS_HARDWARETIMESTAMP_H
#define CDSLIB_COMPILER_GCC_X86_TS_HARDWARETIMESTAMP_H

#include <cstdint>
#include <cpuid.h>

namespace cds { namespace tshardwaretimestamp {
namespace gcc { namespace x86 {

# define CDS_ts_hardwaretimestamp_hwptime_defined
static inline uint64_t get_hwptime()
{
uint64_t ret;
__asm__ volatile ("rdtscp\n" : "=A" (ret) : : "ecx");
return ret;
}

# define CDS_ts_hardwaretimestamp_hwtime_defined
static inline uint64_t get_hwtime()
{
uint64_t ret;
__asm__ volatile("rdtsc" : "=A"(ret));
return ret;
}

static inline int has_rdtscp()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's quit strange to have "has_..." function returning logically boolean value with int signature

{
unsigned int eax, ebx, ecx, edx;
if (__get_cpuid(0x80000001, &eax, &ebx, &ecx, &edx))
return (edx >> 27) & 0x1;
else
return 0;
}

}} // namespace gcc::x86

namespace platform {
using namespace gcc::x86;
}
}} // namespace cds::tshardwaretimestamp

#endif // #ifndef CDSLIB_COMPILER_GCC_X86_TS_HARDWARETIMESTAMP_H
16 changes: 16 additions & 0 deletions cds/compiler/ts_hardwaretimestamp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef CDSLIB_COMPILER_TS_HARDWARETIMESTAMP_H
#define CDSLIB_COMPILER_TS_HARDWARETIMESTAMP_H

// Choose appropriate header for current architecture and compiler

#if CDS_COMPILER == CDS_COMPILER_GCC || CDS_COMPILER == CDS_COMPILER_CLANG || CDS_COMPILER == CDS_COMPILER_INTEL
# if CDS_PROCESSOR_ARCH == CDS_PROCESSOR_X86
# include <cds/compiler/gcc/x86/ts_hardwaretimestamp.h>
# elif CDS_PROCESSOR_ARCH == CDS_PROCESSOR_AMD64
# include <cds/compiler/gcc/amd64/ts_hardwaretimestamp.h>
# endif
#else
# error "Undefined compiler"
#endif

#endif // #ifndef CDSLIB_COMPILER_TS_HARDWARETIMESTAMP_H
225 changes: 225 additions & 0 deletions cds/container/ts_deque.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
#ifndef CDSLIB_CONTAINER_TS_DEQUE_H
#define CDSLIB_CONTAINER_TS_DEQUE_H

#include <atomic>
#include <cds/opt/options.h>
#include <cds/container/ts_deque_buffer.h>

namespace cds { namespace container {

/// TSDeque related definitions
/** @ingroup cds_nonintrusive_helper
*/
namespace tsdeque {
/// TSDeque default type traits
struct traits
{
/// Item counting feature; by default, disabled. Use \p cds::atomicity::item_counter to enable item counting
typedef cds::atomicity::empty_item_counter item_counter;

/// Random engine to generate a random position in array of thread-local buffers
typedef opt::v::c_rand random_engine;
};

/// Metafunction converting option list to \p tsdeque::traits
/**
Supported \p Options are:
- opt::item_counter - the type of item counting feature.
Default is \p cds::atomicity::empty_item_counter (item counting disabled).
- opt::random_engine - a random engine to generate a random position in array of thread-local buffers.
Default is \p opt::v::c_rand.

Example: declare \p %TSDeque with item counting
\code
typedef cds::container::TSDeque< Foo,
typename cds::container::tsdeque::make_traits<
cds::opt::item_counter< cds::atomicity::item_counter >
>::type
> myDeque;
\endcode
*/
template <typename... Options>
struct make_traits {
# ifdef CDS_DOXYGEN_INVOKED
typedef implementation_defined type; ///< Metafunction result
# else
typedef typename cds::opt::make_options<
typename cds::opt::find_type_traits< traits, Options... >::type
, Options...
>::type type;
# endif
};

} // namespace tsdeque

/// Fast Concurrent Deque Through Explicit Timestamping
/** @ingroup cds_nonintrusive_deque

<b>Source</b>
- [2014] Mike Dodds, Andreas Haas, Christoph M. Kirsch
"Fast Concurrent Data-Structures Through Explicit Timestamping"

<b>Template arguments</b>
- \p T - value type to be stored in the deque
- \p Timestamp - the way to acquire timestamps
- \p Traits - deque traits, default is \p tsdeque::traits. You can use \p tsdeque::make_traits
metafunction to make your traits or just derive your traits from \p %tsdeque::traits:
\code
struct myTraits: public cds::container::tsdeque::traits {
typedef cds::atomicity::item_counter item_counter;
};
typedef cds::container::TSDeque< Foo, Timestamp, myTraits > myDeque;

// Equivalent make_traits example:
typedef cds::container::TSDeque< Foo, Timestamp
typename cds::container::tsdeque::make_traits<
cds::opt::item_counter< cds::atomicity::item_counter >
>::type
> myDeque;
\endcode
*/
template <typename T, typename Timestamp, typename Traits = tsdeque::traits>
class TSDeque
{
public:
typedef T value_type; ///< Type of value to be stored in the deque
typedef Timestamp timestamp; ///< Algorithm of acquiring timestamps
typedef Traits traits; ///< Deque traits

typedef typename traits::item_counter item_counter; ///< Item counting policy used
typedef typename traits::random_engine random_engine; ///< Random engine used

private:
TSDequeBuffer<value_type, timestamp, random_engine> *buffer_;
timestamp *timestamping_;
item_counter item_counter_;

public:
TSDeque(uint64_t num_threads, uint64_t delay)
{
timestamping_ = new timestamp();
timestamping_->initialize(delay, num_threads);

buffer_ = new TSDequeBuffer<value_type, timestamp, random_engine>(num_threads, timestamping_);
}

~TSDeque()
{
clear();

delete timestamping_;
delete buffer_;
}

/// Inserts a new element at the left end of the deque container
/**
The function always returns \p true
*/
bool insert_left(value_type element)
{
std::atomic<uint64_t> *item = buffer_->insert_left(element);
timestamping_->set_timestamp(item);
++item_counter_;
return true;
}

/// Inserts a new element at the right end of the deque container
/**
The function always returns \p true
*/
bool insert_right(value_type element)
{
std::atomic<uint64_t> *item = buffer_->insert_right(element);
timestamping_->set_timestamp(item);
++item_counter_;
return true;
}

/// Removes the element from the left end of the deque container
/**
The function returns \p false if the deque is empty, \p true otherwise.
*/
bool remove_left(value_type *element)
{
uint64_t invocation_time[2];
timestamping_->read_time(invocation_time);
bool empty;
while (buffer_->try_remove_left(element, invocation_time, &empty))
{
if (!empty)
{
--item_counter_;
return true;
}
}
return false;
}

/// Removes the element from the right end of the deque container
/**
The function returns \p false if the deque is empty, \p true otherwise.
*/
bool remove_right(value_type *element)
{
uint64_t invocation_time[2];
timestamping_->read_time(invocation_time);
bool empty;
while (buffer_->try_remove_right(element, invocation_time, &empty))
{
if (!empty)
{
--item_counter_;
return true;
}
}
return false;
}

/// Clears the deque (non-atomic)
/**
The function erases all items from the deque.

The function is not atomic. It cleans up the deque and then resets the item counter to zero.
If there are a thread that performs insertion while \p clear is working the result is undefined in general case:
<tt> empty() </tt> may return \p true but the deque may contain item(s).
Therefore, \p clear may be used only for debugging purposes.
*/
void clear()
{
value_type item;
while (remove_right(&item)) {}
item_counter_.reset();
}

/// Checks if the deque is empty
/**
@warning If you use \p atomicity::empty_item_counter in \p traits::item_counter,
the function always returns \p true.
*/
bool empty() const
{
return size() == 0;
}

/// Returns item count in the deque
/**
@warning If you use \p atomicity::empty_item_counter in \p traits::item_counter,
the function always returns 0.
*/
size_t size() const
{
return item_counter_;
}

//@cond
/// The class has no internal statistics. For test consistency only
std::nullptr_t statistics() const
{
return nullptr;
}
//@endcond
};

}} // namespace cds::container

#endif // #ifndef CDSLIB_CONTAINER_TS_DEQUE_H
Loading