main.cpp
#include <iostream>
#include "skiplist.h"
#define FILE_PATH "./store/dumpFile"
int main() {
// 键值中的key用int型,如果用其他类型,需要自定义比较函数
// 而且如果修改key的类型,同时需要修改skipList.load_file函数
SkipList<int, std::string> skipList(6);
skipList.insert_element(1, "好好");
skipList.insert_element(3, "学");
skipList.insert_element(7, "习");
skipList.insert_element(8, "天天");
skipList.insert_element(9, "向");
skipList.insert_element(19, "上");
skipList.insert_element(19, "!");
std::cout << "skipList size:" << skipList.size() << std::endl;
skipList.dump_file();
// skipList.load_file();
skipList.search_element(9);
skipList.search_element(18);
skipList.display_list();
skipList.delete_element(3);
skipList.delete_element(7);
std::cout << "skipList size:" << skipList.size() << std::endl;
skipList.display_list();
}
skiplist.h
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <mutex>
#include <fstream>
#define STORE_FILE "store/dumpFile"
std::mutex mtx;
std::string delimiter = ":";
//class template to implement node
template<typename K, typename V>
class Node{
public:
Node(){}
Node(K k, V v,int);
~Node();
K get_key() const;
V get_value() const;
void set_value(V);
//Linear array to hold pointers to next of different level
Node<K,V> **forward;
int node_level;
private:
K key;
V value;
};
template<typename K, typename V>
Node<K, V>::Node(const K k,const V v,int level){
this->key = k;
this ->value = v;
this ->node_level = level;
//level+1,因为数组的下表是从0-level
this->forward = new Node<K,V>*[level+1];
//把forward数组里面初始化为0
memset(this->forward,0,sizeof(Node<K,V>*)*(level+1));
};
template<typename K, typename V>
Node<K,V>::~Node(){
delete []forward;
};
template<typename K, typename V>
K Node<K,V>::get_key() const{
return key;
};
template<typename K, typename V>
V Node<K,V>::get_value() const{
return value;
};
template<typename K, typename V>
void Node<K,V>::set_value(V value){
this->value=value ;
};
//class template for skip list
template<typename K,typename V>
class SkipList{
public:
SkipList(int);
~SkipList();
int get_random_level();
Node<K,V>* create_note(K,V,int);
int insert_element(K,V);
void display_list();
bool search_element(K);
void delete_element(K);
//转储
void dump_file();
void load_file();
int size();
private:
void get_key_value_from_string(const std::string& str,std::string* key,std::string* value);
bool is_valid_string(const std::string& str);
//maximum level of the skip list
int _max_level;
//current level of skip list
int _skip_list_level;
//pointer to header node
Node<K,V> *_header;
//file operator
std::ofstream _file_writer;
std::ifstream _file_reader;
//skiplist current element count
int _element_count;
};
//create new node
template<typename K,typename V>
Node<K,V>* SkipList<K,V>::create_note(const K k,const V v,int level){
Node<K,V> *n= new Node<K,V>(k,v,level);
return n;
}
// Insert given key and value in skip list
// return 1 means element exists
// return 0 means insert successfully
/*
+------------+
| insert 50 |
+------------+
level 4 +-->1+ 100
|
| insert +----+
level 3 1+-------->10+---------------> | 50 | 70 100
| |
| |
level 2 1 10 30 | 50 | 70 100
| |
| |
level 1 1 4 10 30 | 50 | 70 100
| |
| |
level 0 1 4 9 10 30 40 | 50 | 60 70 100
+----+
*/
template<typename K, typename V>
int SkipList<K,V>::insert_element(const K key,const V value){
mtx.lock();
Node<K,V>* current = this->_header;
//创建更新数组,初始化数组
//更新数组中是node->forward[i]要放的值,也就是小于key的最大值
Node<K,V> *update[_max_level+1];
memset(update,0,sizeof(Node<K,V>*)*(_max_level+1));
//从最高层开始插入跳表
for(int i=_skip_list_level;i>=0;--i){
while(current->forward[i] != NULL && current->forward[i]->get_key()<key){
current = current->forward[i];
}
update[i]=current;
}
//在第0层,让forward指向右边的节点,也就是想要插入的值,即大于key的最小值
current = current->forward[0];
//插入的值已经存在
if(current!=NULL && current->get_key() == key){
std::cout<<"key: "<<key<<" ,exists"<<std::endl;
mtx.unlock();
return 1;
}
//当前节点为空时
//当前阶段不为空时,要插入节点在update【0】和current
if(current == NULL || current ->get_key() !=key ){
//为节点生成一个随机的层数
int random_level= get_random_level();
//如果随即层数是大于跳表的目前层数,初始化大于的层数的指针为头指针
if(random_level>_skip_list_level){
for(int i=_skip_list_level+1;i<random_level+1;++i){
update[i]=_header;
}
_skip_list_level=random_level;
}
// 以前面随机的层数建立一个node
Node<K,V>* insert_node = create_note(key,value,random_level);
// 插入节点
for(int i=0;i<=random_level;++i){
insert_node->forward[i]=update[i]->forward[i];
update[i]->forward[i]=insert_node;
}
std::cout<< "Successfully inserted key: "<< key <<" , value: "<<value<<std::endl;
_element_count++;
}
mtx.unlock();
return 0;
}
// 显示跳表
template<typename K,typename V>
void SkipList<K,V>::display_list(){
std::cout << "\n*****Skip List*****"<<"\n";
for(int i=0;i<= _skip_list_level;++i){
Node<K,V>*node =this->_header->forward[i];
std::cout << "Level " << i << ": ";
while(node!=NULL){
std::cout<<node->get_key()<<": "<<node->get_value()<<";";
node=node->forward[i];
}
std::cout << std::endl;
}
}
// 转存数据到文件
template<typename K,typename V>
void SkipList<K,V>::dump_file(){
std::cout << "dump_file-----------------" << std::endl;
_file_writer.open(STORE_FILE);
Node<K,V>* node=this->_header->forward[0];
while(node!=NULL){
_file_writer << node->get_key()<<":"<<node->get_value()<<"\n";
std::cout<<node->get_key()<<":"<<node->get_value()<<";\n";
node=node->forward[0];
}
_file_writer.flush();
_file_writer.close();
return ;
}
// 从磁盘加载数据
template<typename K,typename V>
void SkipList<K,V>::load_file(){
_file_reader.open(STORE_FILE);
std::cout << "load_file-----------------" << std::endl;
std::string line;
std::string* key=new std::string();
std::string* value=new std::string();
while(getline(_file_reader,line)){
get_key_value_from_string(line,key,value);
if(key->empty()|| value->empty())
continue;
insert_element(*key,*value);
std::cout << "key:" << *key << "value:" << *value << std::endl;
}
_file_reader.close();
}
// 获取跳表的大小
template<typename K,typename V>
int SkipList<K,V>::size(){
return _element_count;
}
template<typename K,typename V>
void SkipList<K,V>::get_key_value_from_string(const std::string& str,std::string* key,std::string* value){
if(!is_valid_string(str)){
return;
}
*key=str.substr(0,str.find(delimiter));
*value =str.substr(str.find(delimiter)+1,str.length());
}
template<typename K,typename V>
bool SkipList<K,V>::is_valid_string(const std::string& str){
if(str.empty())
return false;
if(str.find(delimiter)==std::string::npos)
return false;
return true;
}
// 从跳表中删除元素
template<typename K,typename V>
void SkipList<K,V>::delete_element(K key){
mtx.lock();
Node<K,V>* current = this->_header;
//创建更新数组,初始化数组
//更新数组中是node->forward[i]要放的值,也就是小于key的最大值
Node<K,V> *update[_max_level+1];
memset(update,0,sizeof(Node<K,V>*)*(_max_level+1));
//从最高层开始遍历跳表
for(int i=_skip_list_level;i>=0;--i){
while(current->forward[i] != NULL && current->forward[i]->get_key()<key){
current = current->forward[i];
}
update[i]=current;
}
//在第0层,找到想要删除值的节点
current = current->forward[0];
if(current!=NULL && current->get_key()==key){
// 从最底层开始遍历,删除每一层中的值
for(int i=0;i<=_skip_list_level;++i){
// 如果这一层中没有该节点,则以上层都没有
if(update[i]->forward[i]!=current)
break;
update[i]->forward[i]=current->forward[i];
}
// 移除没有使用过的层数
while(_skip_list_level >0 &&_header->forward[_skip_list_level]==0)
_skip_list_level--;
std::cout << "Successfully deleted key "<< key << std::endl;
_element_count--;
}
mtx.unlock();
}
// Search for element in skip list
/*
+------------+
| select 60 |
+------------+
level 4 +-->1+ 100
|
|
level 3 1+-------->10+------------------>50+ 70 100
|
|
level 2 1 10 30 50| 70 100
|
|
level 1 1 4 10 30 50| 70 100
|
|
level 0 1 4 9 10 30 40 50+-->60 70 100
*/
template<typename K,typename V>
bool SkipList<K,V>::search_element(K key){
std::cout << "search_element-----------------" << std::endl;
Node<K,V>* current=_header;
// 从跳表的最高层开始查找,比要求值小的最大值
for(int i=_skip_list_level;i>=0;--i){
while (current->forward[i] && current->forward[i]->get_key()<key){
current =current->forward[i];
}
}
// 找到 比要求值小的最大值 的下一个节点
current = current->forward[0];
//如果节点存在
if(current &¤t->get_key()==key){
std::cout << "Found key: " << key << ", value: " << current->get_value() << std::endl;
return true;
}
std::cout << "Not Found Key:" << key << std::endl;
return false;
}
// 构造跳表
template<typename K,typename V>
SkipList<K,V>::SkipList(int max_level){
this->_max_level=max_level;
this->_skip_list_level=0;
this->_element_count=0;
//创建系欸但,初始化kv为空
K k;
V v;
this->_header = new Node<K,V>(k,v,_max_level);
}
template<typename K,typename V>
SkipList<K,V>::~SkipList(){
if(_file_reader.is_open())
_file_reader.close();
if(_file_writer.is_open())
_file_writer.close();
delete _header;
}
template<typename K,typename V>
int SkipList<K,V>::get_random_level(){
int k=1;
while(rand()%2){
++k;
}
k=(k<_max_level)?k:_max_level;
return k;
}
stress_test.cpp
#include <iostream>
#include <chrono>
#include <cstdlib>
#include <pthread.h>
#include <time.h>
#include "../skiplist.h"
#define NUM_THREADS 1
#define TEST_COUNT 100000
SkipList<int,std::string> skipList(18);
void *insertElement(void* threadid){
long tid;
tid =(long)threadid;
std::cout << tid << std::endl;
int tmp=TEST_COUNT/NUM_THREADS;
for(int i=tid*tmp,count=0;count<tmp;++i){
++count;
skipList.insert_element(rand()%TEST_COUNT,"a");
}
pthread_exit(NULL);
}
void *getElement(void* threadid) {
long tid;
tid = (long)threadid;
std::cout << tid << std::endl;
int tmp = TEST_COUNT/NUM_THREADS;
for (int i=tid*tmp, count=0; count<tmp; i++) {
count++;
skipList.search_element(rand() % TEST_COUNT);
}
pthread_exit(NULL);
}
int main()
{
srand(time(NULL));
pthread_t threads[NUM_THREADS];
int rc;
int i;
auto start = std::chrono::high_resolution_clock::now();
for(i =0;i <NUM_THREADS;++i){
std::cout << "main() : creating thread, " << i << std::endl;
rc=pthread_create(&threads[i],NULL,insertElement,(void* )i);
if(rc){
std::cout << "Error:unable to create thread," << rc << std::endl;
exit(-1);
}
}
void *ret;
for(i=0;i<NUM_THREADS;++i){
if(pthread_join(threads[i],&ret)!=0){
perror("pthread_create() error");
exit(3);
}
}
auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed =finish - start;
std::cout << "insert elapsed:" << elapsed.count() << std::endl;
pthread_exit(NULL);
// pthread_t threads[NUM_THREADS];
// int rc;
// int i;
// auto start = std::chrono::high_resolution_clock::now();
// for( i = 0; i < NUM_THREADS; i++ ) {
// std::cout << "main() : creating thread, " << i << std::endl;
// rc = pthread_create(&threads[i], NULL, getElement, (void *)i);
// if (rc) {
// std::cout << "Error:unable to create thread," << rc << std::endl;
// exit(-1);
// }
// }
// void *ret;
// for( i = 0; i < NUM_THREADS; i++ ) {
// if (pthread_join(threads[i], &ret) !=0 ) {
// perror("pthread_create() error");
// exit(3);
// }
// }
// auto finish = std::chrono::high_resolution_clock::now();
// std::chrono::duration<double> elapsed = finish - start;
// std::cout << "get elapsed:" << elapsed.count() << std::endl;
return 0;
}
作者的测试结果
自己的测试结果:
插入十万条数据
读取十万条数据
待优化:
delete的时候没有释放内存。
跳表的key用int型,如果使用其他类型需要自定义比较函数,当然把这块抽象出来更好。
参考项目:https://github.com/youngyangyang04/Skiplist-CPP