Boost::pool

使用示例

  1. #include <iostream>
  2. #include <vector>
  3. #include <list>
  4. #include <boost/pool/object_pool.hpp>
  5. #include <boost/pool/pool_alloc.hpp>
  6. #include <boost/timer/timer.hpp>
  7. using namespace std;
  8. using namespace boost;
  9. const int MAXLENGTH = 100000;
  10. class A
  11. {
  12. public:
  13. A()
  14. {
  15. cout << "Construct: " << endl;
  16. }
  17. A ( int a )
  18. {
  19. cout << "Construct: " << a << endl;
  20. }
  21. ~A()
  22. {
  23. cout << "Destruct" << endl;
  24. }
  25. };
  26. function<void ( void ) > pool_sample = []()
  27. {
  28. cout << "==============================\n";
  29. boost::object_pool<A> p;
  30. A *ptr = p.construct ( 1 );
  31. p.destroy ( ptr );
  32. };
  33. function<void ( void ) > pool_sample_1 = []()
  34. {
  35. cout << "==============================\n";
  36. boost::object_pool<A> p;
  37. A *ptr = p.malloc();
  38. cout << "malloc doesn't invoke constructor and destructor.\n";
  39. ptr = new ( ptr ) A ( 1 );
  40. ptr->~A();
  41. p.free ( ptr );
  42. };
  43. auto test_pool_alloc = []()
  44. {
  45. cout << "==============================\n";
  46. vector<int, pool_allocator<int>> vec1;
  47. vector<int> vec2;
  48. {
  49. cout << "USE pool_allocator:\n";
  50. boost::timer::auto_cpu_timer t1;
  51. for ( int i = 0; i < MAXLENGTH; ++i )
  52. {
  53. vec1.push_back ( i );
  54. vec1.pop_back();
  55. }
  56. }
  57. {
  58. cout << "USE STL allocator:\n";
  59. boost::timer::auto_cpu_timer t2;
  60. for ( int i = 0; i < MAXLENGTH; ++i )
  61. {
  62. vec2.push_back ( i );
  63. vec2.pop_back();
  64. }
  65. }
  66. };
  67. auto test_fast_pool_alloc = []()
  68. {
  69. cout << "==============================\n";
  70. list<int, fast_pool_allocator<int>> vec1;
  71. list<int> vec2;
  72. {
  73. cout << "USE fast_pool_allocator:\n";
  74. boost::timer::auto_cpu_timer t1;
  75. for ( int i = 0; i < MAXLENGTH; ++i )
  76. {
  77. vec1.push_back ( i );
  78. vec1.pop_back();
  79. }
  80. }
  81. {
  82. cout << "USE STL allocator:\n";
  83. boost::timer::auto_cpu_timer t2;
  84. for ( int i = 0; i < MAXLENGTH; ++i )
  85. {
  86. vec2.push_back ( i );
  87. vec2.pop_back();
  88. }
  89. }
  90. };
  91. int main()
  92. {
  93. pool_sample();
  94. pool_sample_1();
  95. test_pool_alloc();
  96. test_fast_pool_alloc();
  97. system ( "pause" );
  98. }

boost::pool 的实现原理

pool去按照一定的增长规则,从操作系统申请一大块内存,称为block,源码中用PODptr表示。

这个PODptr结构将block分为三块,第一块是大块数据区,第二块只有sizeof(void*) 个字节,即指针大小,保存下一个PODptr的指针,第三块保存下一PODptr的长度。最后一个PODptr指针为空。

PODptr的数据区被simple_segregated_storage格式化为许多个小块,称为chunk。一个chunk的大小是定义boost::object_pool时决定的,即 sizeof(T)>sizeof(void)?sizeof(T):sizeof(void)。任意一个chunk未被占用时,使用其前sizeof(void*)个字节作为一个指针指向下一个未被占用的chunk。是的,单向链表。而从pool::malloc,就执行单向链表的删除节点操作,每次都返回首个chunk,因此未进行重新申请block前,malloc都是O(1)。

pool::free(ptr)操作就是找到ptr属于哪个PODptr,然后把ptr添加到单向链表头。

pool::ordered_free(ptr)找到ptr属于哪个PODptr,然后通过插入排序把ptr添加到单向链表。

部分源码

  1. /*
  2. 该函数是simple_segregated_storage的成员函数。第一次看到一下懵逼了,不知其何用意。难道不就是得到 *ptr 的功能吗?!
  3. 事实是,对于一个void*是不能dereference的。因为*ptr你将得到一个void类型,C++不允许void类型。
  4. */
  5. static void * & nextof(void * const ptr)
  6. {
  7. return *(static_cast<void **>(ptr));
  8. }

simple_segregated_storage

  1. //segregate会把给的一个sz大小的内存块,拆分为每个partition_sz大小的多个chunk单元,
  2. //每个chunk的前4字节指向下一个chunk(作为链表的next),而最后一个chunk头指向end。
  3. template <typename SizeType>
  4. void * simple_segregated_storage<SizeType>::segregate(
  5. void * const block,
  6. const size_type sz,
  7. const size_type partition_sz,
  8. void * const end)
  9. {
  10. //找到最后一个chunk
  11. char * old = static_cast<char *>(block)
  12. + ((sz - partition_sz) / partition_sz) * partition_sz;
  13. nextof(old) = end;//把最后一个chunk指向end
  14. if (old == block)
  15. return block;//如果这块内存只有一个chunk就返回
  16. //格式化其他的chunk,使每个chunk的前4字节指向下一个chunk
  17. for (char * iter = old - partition_sz; iter != block;
  18. old = iter, iter -= partition_sz)
  19. nextof(iter) = old;
  20. nextof(block) = old;
  21. return block;
  22. }
  23. //添加一个block时,会把这该块分解成chunk,添加到链表的头部。因为无序,所以复杂度O(1)
  24. void add_block(void * const block,
  25. const size_type nsz, const size_type npartition_sz)
  26. {
  27. first = segregate(block, nsz, npartition_sz, first);
  28. }
  29. //通过find_prev找到这个内存块对应的位置,然后添加进去。复杂度O(n)
  30. void add_ordered_block(void * const block,
  31. const size_type nsz, const size_type npartition_sz)
  32. {
  33. void * const loc = find_prev(block);
  34. if (loc == 0)
  35. add_block(block, nsz, npartition_sz);
  36. else
  37. nextof(loc) = segregate(block, nsz, npartition_sz, nextof(loc));
  38. }
  39. //这个没什么好说的,通过比较地址,找到ptr在当前block中的位置,类似插入排序。
  40. template <typename SizeType>
  41. void * simple_segregated_storage<SizeType>::find_prev(void * const ptr)
  42. {
  43. if (first == 0 || std::greater<void *>()(first, ptr))
  44. return 0;
  45. void * iter = first;
  46. while (true)
  47. {
  48. if (nextof(iter) == 0 || std::greater<void *>()(nextof(iter), ptr))
  49. return iter;
  50. iter = nextof(iter);
  51. }
  52. }
  53. //simple_segregated_storage成员变量。 链表头指针。
  54. void * first;

下段代码从simple_segregated_storage链表中获取内存:

  1. template <typename SizeType>
  2. void * simple_segregated_storage<SizeType>::malloc_n(const size_type n,
  3. const size_type partition_size)
  4. {
  5. if(n == 0)
  6. return 0;
  7. void * start = &first;
  8. void * iter;
  9. do
  10. {
  11. if (nextof(start) == 0)
  12. return 0;
  13. //try_malloc_n会从start开始(不算start)向后申请n个partition_size大小的chunk,返回最后一个chunk的指针
  14. iter = try_malloc_n(start, n, partition_size);
  15. } while (iter == 0);
  16. //此处返回内存chunk头
  17. void * const ret = nextof(start);
  18. //此处是经典的单向链表移除其中一个节点的操作。把该内存的前面chunk头指向该内存尾部chunk头指向的内存。即把该部分排除出链表。
  19. nextof(start) = nextof(iter);
  20. return ret;
  21. }
  22. //start会指向满足条件(连续的n个partition_size大小的chunk内存)的chunk头部,返回最后一个chunk指针。
  23. template <typename SizeType>
  24. void * simple_segregated_storage<SizeType>::try_malloc_n(
  25. void * & start, size_type n, const size_type partition_size)
  26. {
  27. void * iter = nextof(start);
  28. //start后面的块是否是连续的n块partition_size大小的内存
  29. while (--n != 0)
  30. {
  31. void * next = nextof(iter);
  32. //如果next != static_cast<char *>(iter) + partition_size,说明下一块chunk被占用或是到了大块内存(block)的尾部。
  33. if (next != static_cast<char *>(iter) + partition_size)
  34. {
  35. // next == 0 (end-of-list) or non-contiguous chunk found
  36. start = iter;
  37. return 0;
  38. }
  39. iter = next;
  40. }
  41. return iter;
  42. }

class PODptr

如上图,类PODptr指示了一个block结构,这个block大小不一定相同,但都由 chunk data+ next ptr + next block size三部分组成。

  • chunk data部分被构造成一个simple_segregated_storage,切分为多个chunk,是一块连续的内存
  • next ptr 指向下一个block结构,next block size指出了下一个block结构的大小。
  • 也就是说,多个PODptr结构组成一个链表,而PODptr内部由simple_segregated_storage分成一个顺序表。
  • PODptr的大小不固定,增长方式见void * pool<UserAllocator>::malloc_need_resize().
  • 初始化的每个chunk都指向下一个chunk

class pool

  1. //pool 从simple_segregated_storage派生
  2. template <typename UserAllocator>
  3. class pool: protected simple_segregated_storage < typename UserAllocator::size_type >;
  4. //返回父类指针以便调用父类函数,其实就是类型转换
  5. simple_segregated_storage<size_type> & store()
  6. { //! \returns pointer to store.
  7. return *this;
  8. }

在调用pool::malloc只申请一个chunk时,如果有足够空间,使用父类指针调用malloc返回内存,否则就重新申请一个大block。代码简单,就不贴了。

下面代码是申请n个连续的chunk。如果没有连续的n个内存就需要重新分配内存了。分配好的内存,通过add_ordered_block添加到chunks的有序链表,并通过地址大小把刚申请的block放到PODptr链表的排序位置。

  1. template <typename UserAllocator>
  2. void * pool<UserAllocator>::ordered_malloc(const size_type n)
  3. { //! Gets address of a chunk n, allocating new memory if not already available.
  4. //! \returns Address of chunk n if allocated ok.
  5. //! \returns 0 if not enough memory for n chunks.
  6. const size_type partition_size = alloc_size();
  7. const size_type total_req_size = n * requested_size;
  8. const size_type num_chunks = total_req_size / partition_size +
  9. ((total_req_size % partition_size) ? true : false);
  10. void * ret = store().malloc_n(num_chunks, partition_size);
  11. #ifdef BOOST_POOL_INSTRUMENT
  12. std::cout << "Allocating " << n << " chunks from pool of size " << partition_size << std::endl;
  13. #endif
  14. if ((ret != 0) || (n == 0))
  15. return ret;
  16. #ifdef BOOST_POOL_INSTRUMENT
  17. std::cout << "Cache miss, allocating another chunk...\n";
  18. #endif
  19. // Not enough memory in our storages; make a new storage,
  20. BOOST_USING_STD_MAX();
  21. //计算下次申请内存的大小,基本就是乘以2.integer::static_lcm是求最小公倍数。
  22. next_size = max BOOST_PREVENT_MACRO_SUBSTITUTION(next_size, num_chunks);
  23. size_type POD_size = static_cast<size_type>(next_size * partition_size +
  24. integer::static_lcm<sizeof(size_type), sizeof(void *)>::value + sizeof(size_type));
  25. char * ptr = (UserAllocator::malloc)(POD_size);
  26. if (ptr == 0)
  27. {
  28. if(num_chunks < next_size)
  29. {
  30. // Try again with just enough memory to do the job, or at least whatever we
  31. // allocated last time:
  32. next_size >>= 1;
  33. next_size = max BOOST_PREVENT_MACRO_SUBSTITUTION(next_size, num_chunks);
  34. POD_size = static_cast<size_type>(next_size * partition_size +
  35. integer::static_lcm<sizeof(size_type), sizeof(void *)>::value + sizeof(size_type));
  36. ptr = (UserAllocator::malloc)(POD_size);
  37. }
  38. if(ptr == 0)
  39. return 0;
  40. }
  41. const details::PODptr<size_type> node(ptr, POD_size);
  42. // Split up block so we can use what wasn't requested.
  43. if (next_size > num_chunks)
  44. store().add_ordered_block(node.begin() + num_chunks * partition_size,
  45. node.element_size() - num_chunks * partition_size, partition_size);
  46. BOOST_USING_STD_MIN();
  47. if(!max_size)
  48. next_size <<= 1;
  49. else if( next_size*partition_size/requested_size < max_size)
  50. next_size = min BOOST_PREVENT_MACRO_SUBSTITUTION(next_size << 1, max_size*requested_size/ partition_size);
  51. // insert it into the list,
  52. // handle border case.
  53. //对大块block进行排序
  54. if (!list.valid() || std::greater<void *>()(list.begin(), node.begin()))
  55. {
  56. node.next(list);
  57. list = node;
  58. }
  59. else
  60. {
  61. details::PODptr<size_type> prev = list;
  62. while (true)
  63. {
  64. // if we're about to hit the end, or if we've found where "node" goes.
  65. if (prev.next_ptr() == 0
  66. || std::greater<void *>()(prev.next_ptr(), node.begin()))
  67. break;
  68. prev = prev.next();
  69. }
  70. node.next(prev.next());
  71. prev.next(node);
  72. }
  73. // and return it.
  74. return node.begin();
  75. }

下面代码是释放未被占用的块。(一个block任何一个chunk被占用就不会释放)

  1. template <typename UserAllocator>
  2. bool pool<UserAllocator>::release_memory()
  3. { //! pool must be ordered. Frees every memory block that doesn't have any allocated chunks.
  4. //! \returns true if at least one memory block was freed.
  5. // ret is the return value: it will be set to true when we actually call
  6. // UserAllocator::free(..)
  7. bool ret = false;
  8. // This is a current & previous iterator pair over the memory block list
  9. details::PODptr<size_type> ptr = list;
  10. details::PODptr<size_type> prev;
  11. // This is a current & previous iterator pair over the free memory chunk list
  12. // Note that "prev_free" in this case does NOT point to the previous memory
  13. // chunk in the free list, but rather the last free memory chunk before the
  14. // current block.
  15. void * free_p = this->first;
  16. void * prev_free_p = 0;
  17. const size_type partition_size = alloc_size();
  18. // Search through all the all the allocated memory blocks
  19. while (ptr.valid())
  20. {
  21. // At this point:
  22. // ptr points to a valid memory block
  23. // free_p points to either:
  24. // 0 if there are no more free chunks
  25. // the first free chunk in this or some next memory block
  26. // prev_free_p points to either:
  27. // the last free chunk in some previous memory block
  28. // 0 if there is no such free chunk
  29. // prev is either:
  30. // the PODptr whose next() is ptr
  31. // !valid() if there is no such PODptr
  32. // If there are no more free memory chunks, then every remaining
  33. // block is allocated out to its fullest capacity, and we can't
  34. // release any more memory
  35. if (free_p == 0)
  36. break;
  37. // We have to check all the chunks. If they are *all* free (i.e., present
  38. // in the free list), then we can free the block.
  39. bool all_chunks_free = true;
  40. // Iterate 'i' through all chunks in the memory block
  41. // if free starts in the memory block, be careful to keep it there
  42. void * saved_free = free_p;
  43. for (char * i = ptr.begin(); i != ptr.end(); i += partition_size)
  44. {
  45. // If this chunk is not free
  46. if (i != free_p)
  47. {
  48. // We won't be able to free this block
  49. all_chunks_free = false;
  50. // free_p might have travelled outside ptr
  51. free_p = saved_free;
  52. // Abort searching the chunks; we won't be able to free this
  53. // block because a chunk is not free.
  54. break;
  55. }
  56. // We do not increment prev_free_p because we are in the same block
  57. free_p = nextof(free_p);
  58. }
  59. // post: if the memory block has any chunks, free_p points to one of them
  60. // otherwise, our assertions above are still valid
  61. const details::PODptr<size_type> next = ptr.next();
  62. if (!all_chunks_free)
  63. {
  64. if (is_from(free_p, ptr.begin(), ptr.element_size()))
  65. {
  66. std::less<void *> lt;
  67. void * const end = ptr.end();
  68. do
  69. {
  70. prev_free_p = free_p;
  71. free_p = nextof(free_p);
  72. } while (free_p && lt(free_p, end));
  73. }
  74. // This invariant is now restored:
  75. // free_p points to the first free chunk in some next memory block, or
  76. // 0 if there is no such chunk.
  77. // prev_free_p points to the last free chunk in this memory block.
  78. // We are just about to advance ptr. Maintain the invariant:
  79. // prev is the PODptr whose next() is ptr, or !valid()
  80. // if there is no such PODptr
  81. prev = ptr;
  82. }
  83. else
  84. {
  85. // All chunks from this block are free
  86. // Remove block from list
  87. if (prev.valid())
  88. prev.next(next);
  89. else
  90. list = next;
  91. // Remove all entries in the free list from this block
  92. //关键点在这里,释放了一个block之后,会把上一个chunk头修改。
  93. if (prev_free_p != 0)
  94. nextof(prev_free_p) = free_p;
  95. else
  96. this->first = free_p;
  97. // And release memory
  98. (UserAllocator::free)(ptr.begin());
  99. ret = true;
  100. }
  101. // Increment ptr
  102. ptr = next;
  103. }
  104. next_size = start_size;
  105. return ret;
  106. }

pool总结

pool的实现基本就是利用simple_segregated_storage内部实现的维护chunk的链表来实现内存管理的。simple_segregated_storage可以说是pool的核心。pool内部一共维护了两个链表:

  • simple_segregated_storage内部的chunk链表。分配单个chunk时,直接从这个链表拿一个chunk,复杂度O(1)。
  • pool内部有个成员变量details::PODptr<size_type> list;用来维护一个大块内存block的链表。可以知道,一个block内部是连续的,但block之间可以认为是不连续的内存。这个链表相当于一个内存地址索引,主要是为了提高查找效率:对于有序排列的内存池,归还内存时,用来快速判断是属于哪个块的。如果没有这个链表,就需要挨个chunk去判断地址大小。

class object_pool

  1. class object_pool: protected pool<UserAllocator>;

object_pool继承自pool,但和pool的区别是,pool用于申请固定大小的内存,而object_pool用于申请固定类型的内存,并会调用构造函数和析构函数。主要的函数就两个:

调用构造函数,用到了一个placement new的方式,老生常谈。

唯一需要注意的是construct和destroy调用的malloc和free,都是调用的 ordered_mallocordered_free

  1. elem``ent_type * construct(Arg1&, ... ArgN&){...}
  2. element_type * construct()
  3. {
  4. element_type * const ret = (malloc)();
  5. if (ret == 0)
  6. return ret;
  7. try { new (ret) element_type(); }
  8. catch (...) { (free)(ret); throw; }
  9. return ret;
  10. }
  11. element_type * malloc BOOST_PREVENT_MACRO_SUBSTITUTION()
  12. {
  13. return static_cast<element_type *>(store().ordered_malloc());
  14. }

destroy显式调用析构函数去析构,然后把内存还给链表维护。

  1. void destroy(element_type * const chunk)
  2. {
  3. chunk->~T();
  4. (free)(chunk);
  5. }
  6. void free BOOST_PREVENT_MACRO_SUBSTITUTION(element_type * const chunk)
  7. {
  8. store().ordered_free(chunk);
  9. }

class singleton_pool

单例内存池的实现,值得注意的有如下几点:

  • 单线程使用单例时(保证无同步问题),可以通过定义宏BOOST_POOL_NO_MT来取消同步的损耗。
  1. #if !defined(BOOST_HAS_THREADS) || defined(BOOST_NO_MT) || defined(BOOST_POOL_NO_MT)
  2. typedef null_mutex default_mutex;
  • 单例内存池的单例实现如下,通过内部类object_creator调用private函数get_pool(),通过create_object.do_nothing();来保证在main之前实例化静态对象static object_creator create_object;
  1. class singleton_pool
  2. {
  3. public:
  4. ...
  5. private:
  6. typedef boost::aligned_storage<sizeof(pool_type), boost::alignment_of<pool_type>::value> storage_type;
  7. static storage_type storage;
  8. static pool_type& get_pool()
  9. {
  10. static bool f = false;
  11. if(!f)
  12. {
  13. // This code *must* be called before main() starts,
  14. // and when only one thread is executing.
  15. f = true;
  16. new (&storage) pool_type;
  17. }
  18. // The following line does nothing else than force the instantiation
  19. // of singleton<T>::create_object, whose constructor is
  20. // called before main() begins.
  21. create_object.do_nothing();
  22. return *static_cast<pool_type*>(static_cast<void*>(&storage));
  23. }
  24. struct object_creator
  25. {
  26. object_creator()
  27. { // This constructor does nothing more than ensure that instance()
  28. // is called before main() begins, thus creating the static
  29. // T object before multithreading race issues can come up.
  30. singleton_pool<Tag, RequestedSize, UserAllocator, Mutex, NextSize, MaxSize>::get_pool();
  31. }
  32. inline void do_nothing() const
  33. {
  34. }
  35. };
  36. static object_creator create_object;
  37. };

总结

  • 适用范围:频繁申请释放相同大小的内存,如需要频繁的创建同一个类的对象。
  • 优点:可以防止内存碎片、极快,避免频繁申请内存的调用.

boost::pool 的源代码一共就几个文件,简洁明了,读起来也不很难。由于代码时间远早于现代C++(C++11之后)成型,兼容编译器的代码建议忽略。因为重要的是其设计思想:如何通过自构两个链表来提升内存管理效率的。

数据结构很简单。适用场景比较狭窄,跟GC没法比。

boost::pool 库速记的更多相关文章

  1. 定长内存池之BOOST::pool

    内存池可有效降低动态申请内存的次数,减少与内核态的交互,提升系统性能,减少内存碎片,增加内存空间使用率,避免内存泄漏的可能性,这么多的优点,没有理由不在系统中使用该技术. 内存池分类: 1.      ...

  2. boost pool 和 object_pool

    内存池(Memory Pool)是一种内存分配方式.        通常我们习惯直接使用new.malloc等API申请分配内存,这样做的缺点在于:由于所申请内存块的大小不定,当频繁使用时会造成大量的 ...

  3. 如何在WINDOWS下编译BOOST C++库 .

    如何在WINDOWS下编译BOOST C++库 cheungmine 2008-6-25   写出来,怕自己以后忘记了,也为初学者参考.使用VC8.0和boost1.35.0.   1)下载boost ...

  4. Windows下如何使用BOOST C++库 .

    Windows下如何使用BOOST C++库 我采用的是VC8.0和boost_1_35_0.自己重新编译boost当然可以,但是我使用了 http://www.boostpro.com/produc ...

  5. boost::pool与内存池技术

      建议看这个链接的内容:http://cpp.winxgui.com/cn:mempool-example-boost-pool Pool分配是一种分配内存方法,用于快速分配同样大小的内存块,    ...

  6. Boost线程库学习笔记

    一.创建一个线程 创建线程 boost::thread myThread(threadFun); 需要注意的是:参数可以是函数对象或者函数指针.并且这个函数无参数,并返回void类型. 当一个thre ...

  7. Boost正则表达式库regex常用search和match示例 - 编程语言 - 开发者第2241727个问答

    Boost正则表达式库regex常用search和match示例 - 编程语言 - 开发者第2241727个问答 Boost正则表达式库regex常用search和match示例 发表回复   Boo ...

  8. Boost::thread库的使用

    阅读对象 本文假设读者有几下Skills [1]在C++中至少使用过一种多线程开发库,有Mutex和Lock的概念. [2]熟悉C++开发,在开发工具中,能够编译.设置boost::thread库. ...

  9. 一起学习Boost标准库--Boost.StringAlgorithms库

    概述 在未使用Boost库时,使用STL的std::string处理一些字符串时,总是不顺手,特别是当用了C#/Python等语言后trim/split总要封装一个方法来处理.如果没有形成自己的com ...

随机推荐

  1. opencv+python3.4的人脸识别----2017-7-19

    opencv3.1  +  python3.4 第一回合(抄代码,可实现):人脸识别涉及一个级联表,目前能力还无法理解. 流程:1.读取图像---2.转换为灰度图---3.创建级联表---4.对灰度图 ...

  2. 【JQUERY】插件的写法

    1. jquery插件怎么写 $.extend $.fn 2. 写的时候注意些什么

  3. AC自动机总结及板子(不带指针)

    蒟蒻最近想学个AC自动机简直被网上的板子搞疯了,随便点开一个都是带指针的,然而平时用到指针的时候并不多,看到这些代码也完全是看不懂的状态.只好在大概理解后自己脑补(yy)了一下AC自动机的代码,居然还 ...

  4. MySQL优化 - 所需了解的基础知识

    时隔一年半,期间一直想写但却觉得没有实质性的内容可记录,本文为 [高性能MySQL] 的学习日志整理分享(感兴趣建议读原书). 优化应贯穿整个产品开发周期中,开发过程中考虑一些性能问题与影响,总比出问 ...

  5. 设计模式(二) 策略模式Strategy

    策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理,我个人的理解是,具有相同行为不同的行为模式,比如走路,有人速度3m/s,有人100m/s,把他们的具体行走和对象本身 ...

  6. 认真地搞OI

    新博客的开头 OI生涯的开始 #include<cstdio> int main() { puts("Hello world!"); ; }

  7. RabbitMQ入门-Topic模式

    上篇<RabbitMQ入门-Routing直连模式>我们介绍了可以定向发送消息,并可以根据自定义规则派发消息.看起来,这个Routing模式已经算灵活的了,但是,这还不够,我们还有更加多样 ...

  8. Git时光机穿梭之版本回退

    现在,你已经学会了修改文件,然后把修改提交到Git版本库,现在,再练习一次,修改readme.txt文件如下: Git is a distributed version control system. ...

  9. HTTP请求中的Form Data与Request Payload的区别

    前端开发中经常会用到AJAX发送异步请求,对于POST类型的请求会附带请求数据.而常用的两种传参方式为:Form Data 和 Request Payload. GET请求 使用get请求时,参数会以 ...

  10. Vue过渡效果之CSS过渡

    前面的话 Vue 在插入.更新或者移除 DOM 时,提供多种不同方式的应用过渡效果.本文将从CSS过渡transition.CSS动画animation及配合使用第三方CSS动画库(如animate. ...