STL常用序列容器

這裏簡要的記述一下STL常用容器的實現原理,要點等內容。

vector

vector是比較常用的stl容器,用法與數組是非類似,其內部實現是連續空間分配,與數組的不同之處在於可彈性增加空間,而array是靜態空間,分配后不能動態擴展。vecotr的實現較為簡單,主要的關鍵點在於當空間不足時,會新分配當前空間2倍的空間,將舊空間數據拷貝到新空間,然後刪除舊空間。

struct _Vector_impl: public _Tp_alloc_type {
    pointer _M_start;   // 元素頭
    pointer _M_finish;  // 元素尾
    pointer _M_end_of_storage;  // 可用空間尾,
          // 省略部分代碼...
};

這個是向尾部添加元素的代碼實現,可以看到如果當前還有剩餘空間的話,直接在尾部添加,如果沒有剩餘空間,則會動態擴展。

void push_back(const value_type& __x) {
    if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) {
	    _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, __x);
	    ++this->_M_impl._M_finish;
    } else
	  _M_realloc_insert(end(), __x);
}

template<typename _Tp, typename _Alloc>
void vector<_Tp, _Alloc>::_M_realloc_insert(iterator __position, const _Tp& __x) {
    const size_type __len = _M_check_len(size_type(1), "vector::_M_realloc_insert");      // 2倍當前大小
    const size_type __elems_before = __position - begin();
    pointer __new_start(this->_M_allocate(__len));
    pointer __new_finish(__new_start);
    __try {
	  // The order of the three operations is dictated by the C++11
	  // case, where the moves could alter a new element belonging
	  // to the existing vector.  This is an issue only for callers
	  // taking the element by lvalue ref (see last bullet of C++11
	  // [res.on.arguments]).
	  _Alloc_traits::construct(this->_M_impl, __new_start + __elems_before, __x);
	  __new_finish = pointer();

	  __new_finish = std::__uninitialized_move_if_noexcept_a(this->_M_impl._M_start, __position.base(), __new_start, _M_get_Tp_allocator();

	  ++__new_finish;

	  __new_finish = std::__uninitialized_move_if_noexcept_a(__position.base(), this->_M_impl._M_finish, __new_finish, _M_get_Tp_allocator());
	}__catch(...) {
	  if (!__new_finish)
	    _Alloc_traits::destroy(this->_M_impl,
				   __new_start + __elems_before);
	  else
	    std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator());
	  _M_deallocate(__new_start, __len);
	  __throw_exception_again;
	}
      std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator());
      _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start);
      this->_M_impl._M_start = __new_start;
      this->_M_impl._M_finish = __new_finish;
      this->_M_impl._M_end_of_storage = __new_start + __len;
    }

// Called by _M_fill_insert, _M_insert_aux etc.
size_type _M_check_len(size_type __n, const char* __s) const {
    if (max_size() - size() < __n)
	  __throw_length_error(__N(__s));

    const size_type __len = size() + std::max(size(), __n);       // 二倍長
    return (__len < size() || __len > max_size()) ? max_size() : __len;
}

pointer _M_allocate(size_t __n) {
    typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;
    return __n != 0 ? _Tr::allocate(_M_impl, __n) : pointer();
}

使用時可使用[],因為其已實現重載[]

      reference
      operator[](size_type __n) _GLIBCXX_NOEXCEPT {
	      __glibcxx_requires_subscript(__n);
	      return *(this->_M_impl._M_start + __n);
      }

使用時要注意迭代器失效問題,這個在很多STL容器中都有這個問題。

list

鏈表list,與vector不同,元素在內存中不連續分配,不支持隨機存取,好處就是插入與刪除時間複雜度為O(1)。在STL中,其實現的是雙向鏈表,其節點的定義可以看到有前驅和後繼指針,實現也較為簡單。

  /// An actual node in the %list.
template<typename _Tp>
struct _List_node : public __detail::_List_node_base {
    _Tp _M_data;
    _Tp* _M_valptr() { return std::__addressof(_M_data); }
    _Tp const* _M_valptr() const { return std::__addressof(_M_data); }
};

struct _List_node_base {
    _List_node_base* _M_next;
    _List_node_base* _M_prev;

    static void swap(_List_node_base& __x, _List_node_base& __y) _GLIBCXX_USE_NOEXCEPT;
    void _M_transfer(_List_node_base* const __first, _List_node_base* const __last) _GLIBCXX_USE_NOEXCEPT;
    void _M_reverse() _GLIBCXX_USE_NOEXCEPT;
    void _M_hook(_List_node_base* const __position) _GLIBCXX_USE_NOEXCEPT;
    void _M_unhook() _GLIBCXX_USE_NOEXCEPT;
};

deque

雙端隊列,具體實現不同於vectorlist,它是一小段一小段連續空間,每段連續空間之間通過指針數組(這個數組中存放的是每個連續空間數組的頭指針)串聯起來,這樣就能訪問到所有元素。之所以採用這種存儲布局,是有原因的,是有其應用場景的,等分析完源碼后,我們就明白其為何要這麼做了。

deque源碼分析

我們摘取部分源碼看一下其實現細節。雙端隊列的迭代器實現代碼如下(相較於vectorlist,對元素的訪問因為其存儲布局不同,在每一段連續分配空間的邊緣要做特殊處理):

#define _GLIBCXX_DEQUE_BUF_SIZE 512       // 默認連續空間大小

  _GLIBCXX_CONSTEXPR inline size_t __deque_buf_size(size_t __size) { 
        return (__size < _GLIBCXX_DEQUE_BUF_SIZE ? size_t(_GLIBCXX_DEQUE_BUF_SIZE / __size) :size_t(1)); 
    }
template<typename _Tp, typename _Ref, typename _Ptr>
    struct _Deque_iterator {
      typedef _Deque_iterator<_Tp, _Tp&, _Tp*>	     iterator;
      typedef _Deque_iterator<_Tp, const _Tp&, const _Tp*> const_iterator;
      typedef _Tp*					 _Elt_pointer;
      typedef _Tp**					_Map_pointer;

      static size_t _S_buffer_size() _GLIBCXX_NOEXCEPT { 
            return __deque_buf_size(sizeof(_Tp)); 
      }

      typedef std::random_access_iterator_tag	iterator_category;
      typedef _Tp				value_type;
      typedef _Ptr				pointer;
      typedef _Ref				reference;
      typedef size_t				size_type;
      typedef ptrdiff_t				difference_type;
      typedef _Deque_iterator			_Self;

      _Elt_pointer _M_cur;          // 當前位置
      _Elt_pointer _M_first;        // 每一小段空間的開始
      _Elt_pointer _M_last;         // 每一小段空間的結束
      _Map_pointer _M_node;         // 指針數組,可通過這裏訪問到所有連續存儲空間片段

      // 構造函數
      _Deque_iterator(_Elt_pointer __x, _Map_pointer __y) _GLIBCXX_NOEXCEPT : _M_cur(__x), _M_first(*__y),
_M_last(*__y + _S_buffer_size()), _M_node(__y) { }

      _Deque_iterator() _GLIBCXX_NOEXCEPT: _M_cur(), _M_first(), _M_last(), _M_node() { }

      _Deque_iterator(const iterator& __x) _GLIBCXX_NOEXCEPT: _M_cur(__x._M_cur), _M_first(__x._M_first),
	_M_last(__x._M_last), _M_node(__x._M_node) { }

      iterator _M_const_cast() const _GLIBCXX_NOEXCEPT {
          return iterator(_M_cur, _M_node);     // 返回當前元素迭代器
      }

      reference operator*() const _GLIBCXX_NOEXCEPT { 
          return *_M_cur; 
      }

      pointer operator->() const _GLIBCXX_NOEXCEPT { 
          return _M_cur; 
      }

      // 重載++運算符,可以看到,當_M_cur指向本段連續空間尾部時,訪問下一個元素的話是下一段連續空間的首地址
      _Self& operator++() _GLIBCXX_NOEXCEPT {   
	    ++_M_cur;     
	    if (_M_cur == _M_last) {  
	        _M_set_node(_M_node + 1);   // 移向下一段連續存儲空間
	        _M_cur = _M_first;          // 下一段連續空間的首元素
	    }
	    return *this;
      }

      _Self operator++(int) _GLIBCXX_NOEXCEPT {
	    _Self __tmp = *this;
	    ++*this;
	    return __tmp;
      }

      _Self& operator--() _GLIBCXX_NOEXCEPT {
	    if (_M_cur == _M_first) {             // 與++類似,如果當前是第一個元素,--時,就應該調到上一個連續存儲空間
	        _M_set_node(_M_node - 1);
	        _M_cur = _M_last;           // 移到上一段空間的最後,
	    }
	    --_M_cur;                       // 因為是[start, last)區間,這裏要--_M_cur;
	    return *this;
      }

      _Self operator--(int) _GLIBCXX_NOEXCEPT {
	    _Self __tmp = *this;
	    --*this;
	    return __tmp;
      }

      _Self& operator+=(difference_type __n) _GLIBCXX_NOEXCEPT {
	    const difference_type __offset = __n + (_M_cur - _M_first);
	    if (__offset >= 0 && __offset < difference_type(_S_buffer_size()))  // 如果當前連續空間滿足
	       _M_cur += __n;
	    else {  // 如果當前段連續空間不夠用了,需要計算跳到連續空間
	        const difference_type __node_offset = __offset > 0 ? __offset / difference_type(_S_buffer_size()) : -difference_type((-__offset - 1) / _S_buffer_size()) - 1;
	        _M_set_node(_M_node + __node_offset);
	        _M_cur = _M_first + (__offset - __node_offset * difference_type(_S_buffer_size()));
	    }
	    return *this;
      }

      _Self operator+(difference_type __n) const _GLIBCXX_NOEXCEPT {
	    _Self __tmp = *this;
	    return __tmp += __n;
      }

      _Self& operator-=(difference_type __n) _GLIBCXX_NOEXCEPT {
          return *this += -__n; }

      _Self operator-(difference_type __n) const _GLIBCXX_NOEXCEPT {
	    _Self __tmp = *this;
	    return __tmp -= __n;
      }

      reference operator[](difference_type __n) const _GLIBCXX_NOEXCEPT { return *(*this + __n); }

      // Prepares to traverse new_node.  Sets everything except _M_cur, which should therefore be set by the caller immediately afterwards, based on _M_first and _M_last.
      void _M_set_node(_Map_pointer __new_node) _GLIBCXX_NOEXCEPT {     // 跳到新的一段連續存儲空間
	    _M_node = __new_node;
	    _M_first = *__new_node;
	    _M_last = _M_first + difference_type(_S_buffer_size());
      }
    };

從上面deque迭代器的實現來看,主要需要注意的地方就是每段連續空間的邊緣。看完迭代器后,我們看一下deque類的實現代碼,這裏刪減掉大部分代碼,保留部分代碼。其中重點看一下deque中最常用的push_frontpop_frontpush_backpop_back的實現。push_back時間複雜度O(1)比較好理解,過程類似於vector,但push_front為什麼也是O(1)呢?如果在頭部插入一個元素,第一個連續空間距離起始start還有剩餘空間的的話,直接插入就好了,如果沒有剩餘空間的話,就創建一段新的連續空間,將首地址放到map中,如果map沒有空間放置這個首地址,就調整map,再插入首地址,詳細過程請看源碼的具體實現:

 template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
    class deque : protected _Deque_base<_Tp, _Alloc> {
      typedef _Deque_base<_Tp, _Alloc>			_Base;
      typedef typename _Base::_Tp_alloc_type		_Tp_alloc_type;
      typedef typename _Base::_Alloc_traits		_Alloc_traits;
      typedef typename _Base::_Map_pointer		_Map_pointer;

    public:
      typedef _Tp					value_type;
      typedef typename _Alloc_traits::pointer		pointer;
      typedef typename _Alloc_traits::const_pointer	const_pointer;
      typedef typename _Alloc_traits::reference		reference;
      typedef typename _Alloc_traits::const_reference	const_reference;
      typedef typename _Base::iterator			iterator;
      typedef typename _Base::const_iterator		const_iterator;
      typedef std::reverse_iterator<const_iterator>	const_reverse_iterator;
      typedef std::reverse_iterator<iterator>		reverse_iterator;
      typedef size_t					size_type;
      typedef ptrdiff_t					difference_type;
      typedef _Alloc					allocator_type;

    protected:
      static size_t _S_buffer_size() _GLIBCXX_NOEXCEPT { return __deque_buf_size(sizeof(_Tp)); }

      // Functions controlling memory layout, and nothing else.
      using _Base::_M_initialize_map;
      using _Base::_M_create_nodes;
      using _Base::_M_destroy_nodes;
      using _Base::_M_allocate_node;
      using _Base::_M_deallocate_node;
      using _Base::_M_allocate_map;
      using _Base::_M_deallocate_map;
      using _Base::_M_get_Tp_allocator;

      /**
       *  A total of four data members accumulated down the hierarchy.
       *  May be accessed via _M_impl.*
       */
      using _Base::_M_impl;

    public:
	  // 省略構造函數與析構函數......

      /*
       *  @brief  Assigns a given value to a %deque.
       *  @param  __n  Number of elements to be assigned.
       *  @param  __val  Value to be assigned.
       *
       *  This function fills a %deque with @a n copies of the given
       *  value.  Note that the assignment completely changes the
       *  %deque and that the resulting %deque's size is the same as
       *  the number of elements assigned.
       */
      void assign(size_type __n, const value_type& __val) { _M_fill_assign(__n, __val); }

	  // 省略其他assign重載函數......	


      /// Get a copy of the memory allocation object.
      allocator_type get_allocator() const _GLIBCXX_NOEXCEPT{ return _Base::get_allocator(); }

      // iterators
      /**
       *  Returns a read/write iterator that points to the first element in the
       *  %deque.  Iteration is done in ordinary element order.
       */
      iterator begin() _GLIBCXX_NOEXCEPT { return this->_M_impl._M_start; }
      const_iterator begin() const _GLIBCXX_NOEXCEPT { return this->_M_impl._M_start; }

      /**
       *  Returns a read/write iterator that points one past the last
       *  element in the %deque.  Iteration is done in ordinary
       *  element order.
       */
      iterator end() _GLIBCXX_NOEXCEPT{ return this->_M_impl._M_finish; }
      const_iterator end() const _GLIBCXX_NOEXCEPT { return this->_M_impl._M_finish; }


	// 省略其他迭代器相關代碼......

      // [23.2.1.2] capacity
      /**  Returns the number of elements in the %deque.  */
      size_type size() const _GLIBCXX_NOEXCEPT { return this->_M_impl._M_finish - this->_M_impl._M_start; }

      /**  Returns the size() of the largest possible %deque.  */
      size_type max_size() const _GLIBCXX_NOEXCEPT { return _Alloc_traits::max_size(_M_get_Tp_allocator()); }

      /**
       *  @brief  Resizes the %deque to the specified number of elements.
       *  @param  __new_size  Number of elements the %deque should contain.
       *
       *  This function will %resize the %deque to the specified
       *  number of elements.  If the number is smaller than the
       *  %deque's current size the %deque is truncated, otherwise
       *  default constructed elements are appended.
       */
      void resize(size_type __new_size) {
		  const size_type __len = size();
		  if (__new_size > __len)
	  		  _M_default_append(__new_size - __len);
		  else if (__new_size < __len)
	  		  _M_erase_at_end(this->_M_impl._M_start + difference_type(__new_size));
      }

#if __cplusplus >= 201103L
      /**  A non-binding request to reduce memory use.  */
      void shrink_to_fit() noexcept { _M_shrink_to_fit(); }
#endif

      /**
       *  Returns true if the %deque is empty.  (Thus begin() would
       *  equal end().)
       */
      bool empty() const _GLIBCXX_NOEXCEPT { return this->_M_impl._M_finish == this->_M_impl._M_start; }

      // element access
      /**
       *  @brief Subscript access to the data contained in the %deque.
       *  @param __n The index of the element for which data should be
       *  accessed.
       *  @return  Read/write reference to data.
       *
       *  This operator allows for easy, array-style, data access.
       *  Note that data access with this operator is unchecked and
       *  out_of_range lookups are not defined. (For checked lookups
       *  see at().)
       */
      reference operator[](size_type __n) _GLIBCXX_NOEXCEPT {
		  __glibcxx_requires_subscript(__n);
		  return this->_M_impl._M_start[difference_type(__n)];
      }

    protected:
      /// Safety check used only from at().
      void _M_range_check(size_type __n) const {
		  if (__n >= this->size())
	  		  __throw_out_of_range_fmt(__N("deque::_M_range_check: __n "
				       "(which is %zu)>= this->size() "
				       "(which is %zu)"), __n, this->size());
      }

    public:
      /**
       *  @brief  Provides access to the data contained in the %deque.
       *  @param __n The index of the element for which data should be
       *  accessed.
       *  @return  Read/write reference to data.
       *  @throw  std::out_of_range  If @a __n is an invalid index.
       *
       *  This function provides for safer data access.  The parameter
       *  is first checked that it is in the range of the deque.  The
       *  function throws out_of_range if the check fails.
       */
      reference at(size_type __n) {
		  _M_range_check(__n);
		  return (*this)[__n];
      }

      /**
       *  @brief  Provides access to the data contained in the %deque.
       *  @param __n The index of the element for which data should be
       *  accessed.
       *  @return  Read-only (constant) reference to data.
       *  @throw  std::out_of_range  If @a __n is an invalid index.
       *
       *  This function provides for safer data access.  The parameter is first
       *  checked that it is in the range of the deque.  The function throws
       *  out_of_range if the check fails.
       */
      const_reference at(size_type __n) const {
		  _M_range_check(__n);
		  return (*this)[__n];
      }

      /**
       *  Returns a read/write reference to the data at the first
       *  element of the %deque.
       */
      reference front() _GLIBCXX_NOEXCEPT {
		  __glibcxx_requires_nonempty();
		  return *begin();
      }

      /**
       *  Returns a read/write reference to the data at the last element of the
       *  %deque.
       */
      reference back() _GLIBCXX_NOEXCEPT {
		  __glibcxx_requires_nonempty();
		  iterator __tmp = end();
		  --__tmp;
		  return *__tmp;
      }


      /**
       *  @brief  Add data to the front of the %deque.
       *  @param  __x  Data to be added.
       *
       *  This is a typical stack operation.  The function creates an
       *  element at the front of the %deque and assigns the given
       *  data to it.  Due to the nature of a %deque this operation
       *  can be done in constant time.
       */
      void push_front(const value_type& __x) {		// 如果第一段連續空間頭部還有剩餘空間的話,直接插入元素
		  if (this->_M_impl._M_start._M_cur != this->_M_impl._M_start._M_first) {
	      	_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_start._M_cur - 1, __x);
	      	--this->_M_impl._M_start._M_cur;
	  	  } else		// 如果沒有,在前部重新分配空間
	    	  _M_push_front_aux(__x);
      }

      /**
       *  @brief  Add data to the end of the %deque.
       *  @param  __x  Data to be added.
       *
       *  This is a typical stack operation.  The function creates an
       *  element at the end of the %deque and assigns the given data
       *  to it.  Due to the nature of a %deque this operation can be
       *  done in constant time.
       */
      void push_back(const value_type& __x) {
		  if (this->_M_impl._M_finish._M_cur != this->_M_impl._M_finish._M_last - 1) {
	    	  _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish._M_cur, __x);
	          ++this->_M_impl._M_finish._M_cur;
	  	  } else 
			_M_push_back_aux(__x);
      }

      /**
       *  @brief  Removes first element.
       *
       *  This is a typical stack operation.  It shrinks the %deque by one.
       *
       *  Note that no data is returned, and if the first element's data is
       *  needed, it should be retrieved before pop_front() is called.
       */
      void pop_front() _GLIBCXX_NOEXCEPT {
		  __glibcxx_requires_nonempty();
		  if (this->_M_impl._M_start._M_cur != this->_M_impl._M_start._M_last - 1) {
	    	  _Alloc_traits::destroy(this->_M_impl,	this->_M_impl._M_start._M_cur);
	    	  ++this->_M_impl._M_start._M_cur;
	  	  } else
	  	  _M_pop_front_aux();
      }

      /**
       *  @brief  Removes last element.
       *
       *  This is a typical stack operation.  It shrinks the %deque by one.
       *
       *  Note that no data is returned, and if the last element's data is
       *  needed, it should be retrieved before pop_back() is called.
       */
      void pop_back() _GLIBCXX_NOEXCEPT {
		  __glibcxx_requires_nonempty();
		  if (this->_M_impl._M_finish._M_cur != this->_M_impl._M_finish._M_first) {
	          --this->_M_impl._M_finish._M_cur;
	    	  _Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish._M_cur);
	  	  } else
	  	  _M_pop_back_aux();
      }

      /**
       *  @brief  Inserts given value into %deque before specified iterator.
       *  @param  __position  An iterator into the %deque.
       *  @param  __x  Data to be inserted.
       *  @return  An iterator that points to the inserted data.
       *
       *  This function will insert a copy of the given value before the
       *  specified location.
       */
      iterator insert(iterator __position, const value_type& __x);

      /**
       *  Erases all the elements.  Note that this function only erases the
       *  elements, and that if the elements themselves are pointers, the
       *  pointed-to memory is not touched in any way.  Managing the pointer is
       *  the user's responsibility.
       */
      void clear() _GLIBCXX_NOEXCEPT { _M_erase_at_end(begin()); }

    protected:
      // Internal constructor functions follow.
	  // 省略部分代碼......
	 	
      void _M_push_back_aux(const value_type&);
      void _M_push_front_aux(const value_type&);
      void _M_pop_back_aux();
      void _M_pop_front_aux();

	  // 省略部分代碼......
    };

deque的實現比vectorlist要複雜的多,主要是因為其空間布局不太一樣。下面的代碼主要是對雙端隊列隊首與隊尾的操作(push_frontpush_backpop_frontpop_back)中涉及到空間變動的部分代碼實現:

// Called only if _M_impl._M_finish._M_cur == _M_impl._M_finish._M_last - 1.
template<typename _Tp, typename _Alloc>
void deque<_Tp, _Alloc>::_M_push_back_aux(const value_type& __t) {
	_M_reserve_map_at_back();
	*(this->_M_impl._M_finish._M_node + 1) = this->_M_allocate_node();      // map新指針指向新分配的連續空間
	__try {
	    this->_M_impl.construct(this->_M_impl._M_finish._M_cur, __t);
	    this->_M_impl._M_finish._M_set_node(this->_M_impl._M_finish._M_node + 1);
	    this->_M_impl._M_finish._M_cur = this->_M_impl._M_finish._M_first;
	} __catch(...) {
	    _M_deallocate_node(*(this->_M_impl._M_finish._M_node + 1));
	    __throw_exception_again;
	}
}

// Called only if _M_impl._M_start._M_cur == _M_impl._M_start._M_first.
template<typename _Tp, typename _Alloc>
void deque<_Tp, _Alloc>::_M_push_front_aux(const value_type& __t) {
	_M_reserve_map_at_front();
	*(this->_M_impl._M_start._M_node - 1) = this->_M_allocate_node();       // map指定位置指向新分配的連續空間
	__try {
	    this->_M_impl._M_start._M_set_node(this->_M_impl._M_start._M_node - 1);
	    this->_M_impl._M_start._M_cur = this->_M_impl._M_start._M_last - 1;
	    this->_M_impl.construct(this->_M_impl._M_start._M_cur, __t);
	} __catch(...) {
	    ++this->_M_impl._M_start;
	    _M_deallocate_node(*(this->_M_impl._M_start._M_node - 1));
	    __throw_exception_again;
	}
}

// Called only if _M_impl._M_finish._M_cur == _M_impl._M_finish._M_first.
template <typename _Tp, typename _Alloc>
void deque<_Tp, _Alloc>::_M_pop_back_aux() {
    _M_deallocate_node(this->_M_impl._M_finish._M_first);
    this->_M_impl._M_finish._M_set_node(this->_M_impl._M_finish._M_node - 1);
    this->_M_impl._M_finish._M_cur = this->_M_impl._M_finish._M_last - 1;
    _Alloc_traits::destroy(_M_get_Tp_allocator(), this->_M_impl._M_finish._M_cur);
}

  // Called only if _M_impl._M_start._M_cur == _M_impl._M_start._M_last - 1.
  // Note that if the deque has at least one element (a precondition for this
  // member function), and if
  //   _M_impl._M_start._M_cur == _M_impl._M_start._M_last,
  // then the deque must have at least two nodes.
  template <typename _Tp, typename _Alloc>
void deque<_Tp, _Alloc>::_M_pop_front_aux() {
    _Alloc_traits::destroy(_M_get_Tp_allocator(), this->_M_impl._M_start._M_cur);
    _M_deallocate_node(this->_M_impl._M_start._M_first);
    this->_M_impl._M_start._M_set_node(this->_M_impl._M_start._M_node + 1);
    this->_M_impl._M_start._M_cur = this->_M_impl._M_start._M_first;
}

下面的原代碼是調整map的,如果map沒有適當空間插入新的連續空間首地址,就重新分配map(這種情況比如,map的後面全部插滿了,但前面還大量空着,就需要將目前的map中的元素進行移動,使map的元素分佈在中間位置,首尾兩端是空閑的,以便於後面插入新元素; 如果是map的空間不足了,則需要新分配map空間,新空間大小要大於新指針元素數量+2)。

void _M_reserve_map_at_back(size_type __nodes_to_add = 1) {
    if (__nodes_to_add + 1 > this->_M_impl._M_map_size - (this->_M_impl._M_finish._M_node - this->_M_impl._M_map))
	    _M_reallocate_map(__nodes_to_add, false);
}

void _M_reserve_map_at_front(size_type __nodes_to_add = 1) {
    if (__nodes_to_add > size_type(this->_M_impl._M_start._M_node - this->_M_impl._M_map))
	    _M_reallocate_map(__nodes_to_add, true);
}

template <typename _Tp, typename _Alloc>
void deque<_Tp, _Alloc>::_M_reallocate_map(size_type __nodes_to_add, bool __add_at_front) {
    const size_type __old_num_nodes = this->_M_impl._M_finish._M_node - this->_M_impl._M_start._M_node + 1;
    const size_type __new_num_nodes = __old_num_nodes + __nodes_to_add;

    _Map_pointer __new_nstart;
    if (this->_M_impl._M_map_size > 2 * __new_num_nodes) {
	    __new_nstart = this->_M_impl._M_map + (this->_M_impl._M_map_size - __new_num_nodes) / 2 + (__add_at_front ? __nodes_to_add : 0);	// 這裏新map的開始往後移動了一段位置,是為了將來在前部插入的時候有剩餘空間,後部空餘一段位置也是。
	    if (__new_nstart < this->_M_impl._M_start._M_node)
	    	std::copy(this->_M_impl._M_start._M_node, this->_M_impl._M_finish._M_node + 1, __new_nstart);
	    else
	    	std::copy_backward(this->_M_impl._M_start._M_node, this->_M_impl._M_finish._M_node + 1, __new_nstart + __old_num_nodes);
	} else {
	    size_type __new_map_size = this->_M_impl._M_map_size + std::max(this->_M_impl._M_map_size, __nodes_to_add) + 2;		// 要至少空餘2個空閑位置
	    _Map_pointer __new_map = this->_M_allocate_map(__new_map_size);
	    __new_nstart = __new_map + (__new_map_size - __new_num_nodes) / 2 + (__add_at_front ? __nodes_to_add : 0);
	    std::copy(this->_M_impl._M_start._M_node, this->_M_impl._M_finish._M_node + 1, __new_nstart);
	    _M_deallocate_map(this->_M_impl._M_map, this->_M_impl._M_map_size);

	    this->_M_impl._M_map = __new_map;
	    this->_M_impl._M_map_size = __new_map_size;
	}

    this->_M_impl._M_start._M_set_node(__new_nstart);
    this->_M_impl._M_finish._M_set_node(__new_nstart + __old_num_nodes - 1);
}

更詳細的還是自己看STL的源碼吧,順便吐槽一下STL的源碼,代碼太臃腫了,看起來太累了,如果按照其實現原理,自己實現一個mini版STL,應該會簡潔許多許多。

到這裏,deque中比較核心的源碼已經基本分析完了,也基本展現了deque中幾個關鍵成員函數是如何實現的,其迭代器的實現,其map的實現與調整。

deque與vector、list的對比

vector能夠實現隨機訪問,動態擴展,但在頭部插入O(n),開銷較大,且動態擴展時需要複製所有的元素,同樣效率較低。list插入、刪除頭尾部元素效率很高O(n),但是不能隨機訪問,查找效率O(n),每個節點需要存儲前後節點指針,有較大的額外存儲開銷。而deque等於是在兩種容器的優缺點進行了一定的平衡,在收尾插入、刪除元素,效率很高O(1),在中間插入O(n)都差不多,但其能實現隨機訪問,且動態擴展時不需要複製全體元素,只需要新分配足夠的連續存儲空間,最多重新複製一下map到新map,而map是各個連續存儲空間首地址指針數組,容量相比全體元素小非常多,動態擴展時代價很小。所以,deque相比vector在更一般的情況下有更高的性能,相比list有更小的額外存儲空間(但deque擁有較大的最小內存開銷,原因是它需要map和一段連續存儲空間開銷,即元素數目非常小時開銷比list大,但當元素數目較多時,空間開銷比list少)。

stack

棧也是經常用的數據結構,其實現較為簡單,內部實現依賴deque,當然也可以用vectorlist實現。

// Stack implementation -*- C++ -*-
template<typename _Tp, typename _Sequence = deque<_Tp> >
class stack {
// concept requirements
typedef typename _Sequence::value_type _Sequence_value_type;

public:
    typedef typename _Sequence::value_type		value_type;
    typedef typename _Sequence::reference		reference;
    typedef typename _Sequence::const_reference	const_reference;
    typedef typename _Sequence::size_type		size_type;
    typedef	       _Sequence			container_type;

protected:
    _Sequence c;

public:
	stack(): c() { }
	// 省略構造函數與析構函數......
      /**
       *  Returns true if the %stack is empty.
       */
    bool empty() const { return c.empty(); }

    /**  Returns the number of elements in the %stack.  */
    size_type size() const { return c.size(); }

      /**
       *  Returns a read/write reference to the data at the first
       *  element of the %stack.
       */
    reference top() {
		__glibcxx_requires_nonempty();
		return c.back();
    }
      /**
       *  @brief  Add data to the top of the %stack.
       *  @param  __x  Data to be added.
       *
       *  This is a typical %stack operation.  The function creates an
       *  element at the top of the %stack and assigns the given data
       *  to it.  The time complexity of the operation depends on the
       *  underlying sequence.
       */
    void push(const value_type& __x) { c.push_back(__x); }

      /**
       *  @brief  Removes first element.
       *
       *  This is a typical %stack operation.  It shrinks the %stack
       *  by one.  The time complexity of the operation depends on the
       *  underlying sequence.
       *
       *  Note that no data is returned, and if the first element's
       *  data is needed, it should be retrieved before pop() is
       *  called.
       */
    void pop() {
		__glibcxx_requires_nonempty();
		c.pop_back();
    }

	// 省略其他非關鍵代碼......
}; 

queue

隊列有普通的先進先出的隊列,還有優先隊列,優先級隊列不僅僅要按先後順序,更強調優先級高的先出隊列。

普通隊列的實現

普通隊列的實現與棧實現差不多,也是基於deque實現的。

template<typename _Tp, typename _Sequence = deque<_Tp> >
class queue {
// concept requirements
typedef typename _Sequence::value_type _Sequence_value_type;

public:
    typedef typename	_Sequence::value_type		value_type;
    typedef typename	_Sequence::reference		reference;
    typedef typename	_Sequence::const_reference	const_reference;
    typedef typename	_Sequence::size_type		size_type;
    typedef		_Sequence			container_type;

protected:
      /*  Maintainers wondering why this isn't uglified as per style
       *  guidelines should note that this name is specified in the standard,
       *  C++98 [23.2.3.1].
       *  (Why? Presumably for the same reason that it's protected instead
       *  of private: to allow derivation.  But none of the other
       *  containers allow for derivation.  Odd.)
       */
       ///  @c c is the underlying container.
    _Sequence c;

public:
	queue(): c() { }
	// 省略構造函數與析構函數......

    bool empty() const { return c.empty(); }
    size_type size() const { return c.size(); }

    reference front() {
		__glibcxx_requires_nonempty();
		return c.front();
    }

    reference back() {
		__glibcxx_requires_nonempty();
		return c.back();
    }

    // Add data to the end of the %queue.
    void push(const value_type& __x) { c.push_back(__x); }
      
    // Removes first element.
    void pop() {
		__glibcxx_requires_nonempty();
		c.pop_front();
    }
};

優先隊列priority_queue實現

優先隊列的實現原理是基於堆實現的,堆底層是數組,所以,這裏priority_queue底層的序列容器是vector,選則vector而不是其他容器,是因為優先隊列基於堆,而堆的各種操作中,插入、刪除、都是從尾部插入、刪除操作最後實際上物理刪除的是尾部元素,而且其擴容是2倍擴容,符合二叉樹下一層節點數目是上一次所有數目+1,二倍擴容恰好合適,當然也可以用其他容器(例如deque,但不是最優的)。至於堆實現優先隊列的原理,這裏不再敘述。源碼實現如下:

template<typename _Tp, typename _Sequence = vector<_Tp>, typename _Compare  = less<typename _Sequence::value_type> >
class priority_queue {
#ifdef _GLIBCXX_CONCEPT_CHECKS
// concept requirements
typedef typename _Sequence::value_type _Sequence_value_type;
# if __cplusplus < 201103L
    __glibcxx_class_requires(_Tp, _SGIAssignableConcept)
# endif
    __glibcxx_class_requires(_Sequence, _SequenceConcept)
    __glibcxx_class_requires(_Sequence, _RandomAccessContainerConcept)
    __glibcxx_class_requires2(_Tp, _Sequence_value_type, _SameTypeConcept)
    __glibcxx_class_requires4(_Compare, bool, _Tp, _Tp, _BinaryFunctionConcept)
#endif

#if __cplusplus >= 201103L
template<typename _Alloc>
using _Uses = typename
enable_if<uses_allocator<_Sequence, _Alloc>::value>::type;
#endif

public:
    typedef typename	_Sequence::value_type		value_type;
    typedef typename	_Sequence::reference		 reference;
    typedef typename	_Sequence::const_reference	   const_reference;
    typedef typename	_Sequence::size_type		 size_type;
    typedef		_Sequence			    container_type;
    typedef	       _Compare				    value_compare;

protected:
     _Sequence  c;
    _Compare   comp;	// 優先隊列基於堆,而堆經常需要比較操作

public:
    //    *  @brief  Default constructor creates no elements.
    explicit priority_queue(const _Compare& __x = _Compare(), const _Sequence& __s = _Sequence()): c(__s), comp(__x) { 		
        std::make_heap(c.begin(), c.end(), comp); 	// 構造堆
	}

	// 省略其他構造函數......

      /**
       *  Returns true if the %queue is empty.
       */
    bool empty() const { 
		return c.empty(); 
	}

      /**  Returns the number of elements in the %queue.  */
    size_type size() const { return c.size(); }

      /**
       *  Returns a read-only (constant) reference to the data at the first
       *  element of the %queue.
       */
    const_reference top() const {
		__glibcxx_requires_nonempty();
		return c.front();
    }

      /**
       *  @brief  Add data to the %queue.
       *  @param  __x  Data to be added.
       *
       *  This is a typical %queue operation.
       *  The time complexity of the operation depends on the underlying
       *  sequence.
       */
    void push(const value_type& __x) {		// 優先隊列中插入元素,先放到容器尾部,再進行“上移”操作使之滿足堆性質。
		c.push_back(__x);
		std::push_heap(c.begin(), c.end(), comp);
    }

      /**
       *  @brief  Removes first element.
       *
       *  This is a typical %queue operation.  It shrinks the %queue
       *  by one.  The time complexity of the operation depends on the
       *  underlying sequence.
       *
       *  Note that no data is returned, and if the first element's
       *  data is needed, it should be retrieved before pop() is
       *  called.
       */
    void pop() {		//從優先隊列中彈出首元素
		__glibcxx_requires_nonempty();
		std::pop_heap(c.begin(), c.end(), comp);
		c.pop_back();
    }
};

可以看到只要理解了堆的實現原理,優先隊列的實現原理就非常容易理解,堆的相關STL源碼分析不在這裏繼續分析。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

08_提升方法_AdaBoost算法

  今天是2020年2月24日星期一。一個又一個意外因素串連起2020這不平凡的一年,多麼希望時間能夠倒退。曾經覺得電視上科比的畫面多麼熟悉,現在全成了陌生和追憶。

GitHub:https://github.com/wangzycloud/statistical-learning-method

提升方法

引入

  提升方法是一種常用的統計學習方法,還是比較容易理解的。在分類問題中,通過改變訓練樣本的權重,學習多個分類器,並將這些分類器進行線性組合,從而提高分類的性能。其實說白了,就是一個人干不好的活,我讓兩個人干;兩個人干不好,那就三個人四個人都來干。但是人多了不能像三個和尚那樣沒水喝,都不幹活。包工頭要採取一些措施,措施一:一個人幹活的時候,哪裡乾的不好?就讓第二個人補充在這個地方;兩個人幹活的時候,哪裡乾的不好?就讓第三個人補充在這個地方。措施二:這三四個人在同一個地方幹活,怎樣確定這個活的結果乾的好不好呢?有的人幹活細緻認真,自然對結果增益大;幹活粗糙的,對結果增益不多,因此需要有一種組合策略進行判斷。通過幾個人共同努力,就解決了一個人干不好的事情。

  接下來的內容,將按照書中順序,先介紹提升方法的思路和代表性的提升算法AdaBoost;再從前向分步加法模型角度解釋AdaBoost;最後看一個具體實例—提升樹。

提升方法AdaBoost算法

  提升方法基於這樣一種思想:對於一個複雜的任務來說,將多個專家的判斷進行適當的綜合所得出的判斷,要比其中任何一個專家單獨的判斷好。實際上,就是“三個臭皮匠頂個諸葛亮“的道理。書中上來提到了兩個稍晦澀的概念“強可學習”、“弱可學習”,下文沒有繼續闡述這兩個概念,這裏簡單的複述一下。在概率近似正確學習的框架中(不懂),一個概念,如果存在一個多項式的學習算法能夠學習它,並且正確率很高,那麼就稱這個概念是強可學習的;一個概念,如果存在一個多項式的學習算法能夠學習它,學習的正確率僅比隨機猜測略好,那麼就稱這個概念是弱可學習的。這兩個概念之間的相互關係,被證明是等價的,也就是說在一定的學習框架下,一個概念是強可學習的充分必要條件是這個概念是弱可學習的。(模型效果好==》強可學習;模型效果差==》弱可學習;兩者有聯繫)

  這樣一來,在學習的過程中如果已經發現了“弱學習算法”,那麼能否將它提升為“強學習算法”。我們知道,發現弱學習算法通常要比發現強學習算法容易得多,如何具體實施提升,是我們開發提升方法時所要解決的問題。

  對於分類問題而言,給定一個訓練數據樣本集,求比較粗糙的分類規則(弱分類器)要比求精確的分類規則容易得多。提升方法就是從弱學習算法出發,反覆學習,得到一系列弱分類器(又稱為基本分類器),然後組合這些弱分類器,構成一個強分類器。並且大多數的提升方法都是改變訓練數據的概率分佈(訓練數據的權值分佈),針對不同的訓練數據分佈調用弱學習算法學習一系列弱分類器。可以理解為難分的數據加大權重,讓下一個弱分類器重點學習該數據。

  這樣,對於提升方法來說,有兩個問題需要解決:一是在每一輪如何改變訓練數據的權值或概率分佈;二是如何將弱分類器組合成一個強分類器。我們接下來要學習的AdaBoost算法,針對第一個問題,提高那些被前一輪分類器錯誤分類樣本的權值,而降低那些被正確分類樣本的權值。這樣一來,那些沒有得到正確分類的數據,由於其權重的加大而受到后一輪弱分類器的更多關注。於是,分類問題被一系列的弱分類器“分而治之”。針對第二個問題,AdaBoost採取加權多數表決的方法。具體的,加大分類誤差率小的弱分類器的權值,使其在表決中起較大的作用;減小分類誤差率大的弱分類器的權值,使其在表決中起到較小的作用。

AdaBoost算法

  這裏先簡單的羅列AdaBoost算法過程,先趕完整體進度,後期編寫代碼時再整理細節。

  假設給定一個二類分類的訓練數據集

AdaBoost算法各個步驟的說明:

  步驟(1)假設訓練數據集具有均勻的權值分佈,即每個訓練樣本在基本分類器的學習中作用相同,這一假設保證第一步能夠在原始數據上學習基本分類器G1(x)。

  步驟(2)AdaBoost反覆學習基本分類器,在每一輪m=1,2,…,M順次執行四步操作:

  第a步,使用當前分佈Dm加權的訓練數據集,學習基本分類器Gm(x);

  第b步,計算基本分類器Gm(x)在加權訓練數據集上的分類誤差率。實際上,第m個分類器的分類誤差率,就是被分類器Gm(x)誤分類樣本的權值之和,這表明權重大的樣本起的作用更大一些。如果將其誤分類,則誤差率大大增加,從而進一步影響Gm(x)的係數;

  第c步,從公式8.8中,可以看出em≤1/2時,αm≥0,並且αm隨着em的減小而增大,所以分類誤差率越小的基本分類器在最終分類器中的作用越大;

  第d步,更新訓練數據的權值分佈為下一輪做準備。可以看到,誤分類樣本在下一輪學習中起更大的作用,不改變所給的訓練數據,而不斷改變訓練數據權值的分佈,使得訓練數據在基本分類器的學習中起不同的作用。

  步驟(3)線性組合f(x)實現M個基本分類器的加權表決。係數αm表示了基本分類器Gm(x)的重要性,這裏所有αm之和並不為1。f(x)的符號決定實例x的類,f(x)的絕對值表示分類的確信度。

AdaBoost的例子

AdaBoost算法的訓練誤差分析

  該訓練誤差分析部分,用證明出來的定理形式,表明AdaBoost算法的最基本性質就是能在學習過程中不斷減少訓練誤差。也就是“該算法能夠降低在訓練數據集的分類誤差率”這件事用數學方式證明了。分別是AdaBoost訓練誤差上界定理8.1、二分類問題的AdaBoost訓練誤差上界定理8.2和推論8.1。

  該定理說明,可以在每一輪選取適當的Gm使得Zm最小,從而使訓練誤差下降的最快。對於二分類問題,有定理8.2。

  該推論表明,在此條件下,AdaBoost的訓練誤差是以指數速率下降的(看着像,不懂)。

AdaBoost算法的解釋

  本節內容從另外一個已經驗證的模型來分析AdaBoost算法,並得到該算法的另外解釋(我的理解是:AdaBoost算法是一般化的加法模型的特例,現在要從加法模型的角度分析)。可以認為AdaBoost算法是模型為加法模型、損失函數為指數函數、學習算法為前向分佈算法時的二類分類學習方法。接下來的內容,先對加法模型及前向分佈算法做簡單介紹,通過對損失函數的分析,得到與AdaBoost算法等價的參數表示。

  考慮加法模型,b(x)函數相當於基分類器:

  在給定訓練數據及損失函數L(y,f(x))的條件下,學習加法模型f(x)成為經驗風險極小化及損失函數極小化問題:

  一般來說,加法模型f(x)中的M次基函數相加模型是一個複雜的優化問題,利用前向分佈算法可以求解該優化問題。前向分佈算法的思想是:因為學習的是加法模型,如果能夠從前向後,每一步只學習一個基函數及其係數,逐步逼近優化目標8.14,那麼就可以簡化優化的複雜度。具體的,每一步只需優化以下損失函數(相當於每一步優化一個基函數,優化一個前進一個):

  那麼,前向分佈算法如下:

  本來公式8.14中的同時求解從m=1到M所有參數β、γ的優化問題,通過前向分佈算法簡化成了逐次求解各個β、γ的優化問題。

  那麼,前向分佈算法與AdaBoost算法有什麼聯繫呢?我們可以用定理的形式敘述之間的聯繫,也就是我們可以由前向分佈算法推導出AdaBoost。定理如下:

  以上內容直接截圖了。本是作為筆記進行整理,數學推導插不上解釋的嘴。要是以後文章被看到的多了,手推一下,再把內容整理上來。

提升樹

  我們知道,提升方法實際上是採用加法模型(即基函數的線性組合)與前向分步算法。本節提升樹是以決策樹作為基函數的提升方法,對分類問題決策樹是二叉分類樹,對回歸問題決策樹是二叉回歸樹。提升樹被認為是統計學習中性能最好的方法之一。

  在例8.1中看到的基本分類器x<v或x>v,可以看作是一個根節點直接連接兩個恭弘=叶 恭弘節點的簡單決策樹,即所謂的決策樹樁。提升樹模型可以表示為決策樹的加法模型:

  即使數據中的輸入與輸出之間的關係很複雜,樹的線性組合都可以很好地擬合訓練數據。分類問題與回歸問題的區別主要在於使用的損失函數不同,回歸問題中一般使用平方誤差損失函數,分類問題一般使用指數損失函數。對於二分類問題,提升樹算法只需要將AdaBoost算法8.1中的基本分類器限製為二類分類即可。下面看一下用於回歸問題的提升樹:

  R是當前模型擬合數據的殘差,所以對回歸問題的提升樹算法來說,只需要簡單的擬合當前模型的殘差即可。具體算法如下:

  具體例子8.2:

  算法8.1代碼已上傳至github,先理解決策樹章節的算法5.5,該提升樹會非常好理解~

代碼效果

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

※回頭車貨運收費標準

RocketMQ系列(二)環境搭建

RocketMQ的基本概念在上一篇中給大家介紹了,這一節將給大家介紹環境搭建。RocketMQ中最基礎的就是NameServer,我們先來看看它是怎麼搭建的。

NameServer

RocketMQ要求的環境是JDK8以上,我們先檢查一下環境,

[root@centOS-1 ~]# java -version
openjdk version "11.0.3" 2019-04-16 LTS
OpenJDK Runtime Environment 18.9 (build 11.0.3+7-LTS)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.3+7-LTS, mixed mode, sharing)

我的這個機器並沒有刻意的安裝JDK,而是系統自帶的OpenJDK 11,這應該也是沒有問題的。然後我們從RocketMQ官網下載最新的安裝包,並且上傳到/opt目錄下,

[root@centOS-1 opt]# ll
-rw-r--r--.  1 root  root 13838456 6月   3 08:49 rocketmq-all-4.7.0-bin-release.zip

然後我們解壓這個zip包,

[root@centOS-1 opt]# unzip rocketmq-all-4.7.0-bin-release.zip

這裏使用的是unzip命令,如果你的機器里沒有這個命令,可以使用yum install安裝一個。解壓以後,進入到RocketMQ的主目錄,並且啟動一下NameServer。

[root@centOS-1 opt]# cd rocketmq-all-4.7.0-bin-release
[root@centOS-1 rocketmq-all-4.7.0-bin-release]# ./bin/mqnamesrv
OpenJDK 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release.
Unrecognized VM option 'UseCMSCompactAtFullCollection'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

這裏出了一個錯誤Error: Could not create the Java Virtual Machine,這是由於RocketMQ的啟動文件都是按照JDK8配置的,而我們這裏使用的是OpenJDK11,有很多命令參數不支持導致的,如果小夥伴們使用的是JDK8,正常啟動是沒有問題的。

在這裏我們改一下RocketMQ的啟動文件,

[root@centOS-1 rocketmq-all-4.7.0-bin-release]# vim bin/runserver.sh 
export JAVA_HOME
export JAVA="$JAVA_HOME/bin/java"
export BASE_DIR=$(dirname $0)/..
#在CLASSPATH中添加RocketMQ的lib目錄
#export CLASSPATH=.:${BASE_DIR}/conf:${CLASSPATH}
export CLASSPATH=.:${BASE_DIR}/lib/*:${BASE_DIR}/conf:${CLASSPATH}

修改的地方我們增加了註釋,在ClassPath里添加了lib目錄,然後在這個文件的末尾,註釋掉升級JDK后不支持的幾個參數,

JAVA_OPT="${JAVA_OPT} -server -Xms4g -Xmx4g -Xmn2g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
#JAVA_OPT="${JAVA_OPT} -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8  -XX:-UseParNewGC"
JAVA_OPT="${JAVA_OPT} -verbose:gc -Xloggc:${GC_LOG_DIR}/rmq_srv_gc_%p_%t.log -XX:+PrintGCDetails"
#JAVA_OPT="${JAVA_OPT} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"
JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow"
JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages"
#JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${JAVA_HOME}/jre/lib/ext:${BASE_DIR}/lib"
#JAVA_OPT="${JAVA_OPT} -Xdebug -Xrunjdwp:transport=dt_socket,address=9555,server=y,suspend=n"
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
JAVA_OPT="${JAVA_OPT} -cp ${CLASSPATH}"

好了,修改完以後,我們保存退出,再次啟動,這次我們在後台啟動NameServer,

[root@centOS-1 rocketmq-all-4.7.0-bin-release]# nohup ./bin/mqnamesrv &

[root@centOS-1 rocketmq-all-4.7.0-bin-release]# tail -500f ~/logs/rocketmqlogs/namesrv.log 

然後查看一下日誌,在日誌中看到main - The Name Server boot success. serializeType=JSON,說明NameServer啟動成功了。

單點的NameServer肯定是不能滿足我們的要求的,怎麼也要做個集群吧。NameServer是一個無狀態的服務,節點之間沒有任何數據往來,所以NameServer的集群搭建不需要任何的配置,只需要啟動多個NameServer服務就可以了,它不像Zookeeper集群搭建那樣,需要配置各個節點。在這裏我們就啟動3個NameServer節點吧,對應我們的3台機器,192.168.73.130,192.168.73.131,192.168.73.132

Broker

NameServer集群搭建完成,下面就搭建Broker了,Broker呢,我們要搭建一個兩主兩從結構的,主從之間異步備份,保存磁盤也是使用異步的方式。如果你對主從同步和保存磁盤的方式還不了解,看看上一節的內容吧。異步兩主兩從這種結構的配置,在RocketMQ中已經有例子了,我們先一下配置文件。

[root@centOS-1 rocketmq-all-4.7.0-bin-release]# vim conf/2m-2s-async/broker-a.properties 

這個配置文件是broker-a“主”的配置文件,

brokerClusterName=RocketMQ-Cluster
brokerName=broker-a
brokerId=0
deleteWhen=04
fileReservedTime=48
brokerRole=ASYNC_MASTER
flushDiskType=ASYNC_FLUSH

其中,

  • brokerClusterName是MQ集群的名稱,我們改為RocketMQ-Cluster。
  • brokerName是隊列的名字,配置為broker-a。
  • brokerId是隊列的id,0代表是“主”,其他正整數代表着“從”。
  • deleteWhen=04 代表着commitLog過期了,就會被刪除。
  • fileReservedTime是commitLog的過期時間,單位是小時,這裏配置的是48小時。
  • brokerRole,隊列的角色,ASYNC_MASTER是異步主。
  • flushDiskType,保存磁盤的方式,異步保存。

再看看broker-a的從配置,

brokerClusterName=RocketMQ-Cluster
brokerName=broker-a
brokerId=1
deleteWhen=04
fileReservedTime=48
brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH

其中,集群的名字一樣,隊列的名字一樣,只是brokerId和brokerRole不一樣,這裏的配置代表着它是隊列broker-a的“從”。broker-b的配置和broker-a是一樣的,只是brokerName不一樣而已,在這裏就不貼出來了。

兩主兩從的配置文件都已經配置好了,我們來規劃一下,我們的NameServer是3台192.168.73.130,192.168.73.131,192.168.73.132,broker按照如下部署:

  • broker-a(主):192.168.73.130
  • broker-a(從):192.168.73.131
  • broker-b(主):192.168.73.131
  • broker-b(從):192.168.73.130

接下來,我們啟動broker,在192.168.73.130上啟動 broker-a(主)和broker-b(從)。和NameServer一樣,我們需要修改一下啟動的腳本,否則也會報錯誤。我們修改的是runbroker.sh這個文件,修改的內容和前面是一樣的,這裏就不贅述了。在啟動文件中,內存大小配置的是8g,如果機器的內存不夠,可以適當減少一下內存。

這裏還要做個說明,由於我們在一台機器上啟動了兩個broker實例,監聽端口和日誌存儲的路徑都會有衝突。那麼我們在192.168.73.130的broker-b(從)的配置文件中,增加配置,如下:

brokerClusterName=RocketMQ-Cluster
brokerName=broker-b
brokerId=1
deleteWhen=04
fileReservedTime=48
brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH

listenPort=11911
storePathRootDir=~/store-b                       

broker-b(從)的端口改為11911,區別默認的10911;storePathRootDir改為~/store-b,區分默認的~/store

同樣在192.168.73.131的broker-a(從)也要做修改,如下:

brokerClusterName=RocketMQ-Cluster
brokerName=broker-a
brokerId=1
deleteWhen=04
fileReservedTime=48
brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH

listenPort=11911
storePathRootDir=~/store-a

然後,我們在192.168.73.130上啟動,如下,

nohup ./bin/mqbroker -c conf/2m-2s-async/broker-a.properties -n '192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876' &

nohup ./bin/mqbroker -c conf/2m-2s-async/broker-b-s.properties -n '192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876' &

  • -c 指定的是配置文件,分別指定的是broker-a(主)和broker-b(從)。
  • -n 指定的是NameServer的地址,指定了3個,用,隔開。

再在192.168.73.131上啟動,如下,

nohup ./bin/mqbroker -c conf/2m-2s-async/broker-b.properties -n '192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876' &

nohup ./bin/mqbroker -c conf/2m-2s-async/broker-a-s.properties -n '192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876' &

好,如果沒有出現錯誤,到這裏,集群就搭建成功了。這裏邊有個小坑,大家一定要注意,就是-n後面的地址一定要用”括起來,並且地址之間要用;,否則,我們在查看集群列表時,是看不到的。

mqadmin

集群已經搭建好了,我們可以查看一下集群的狀態,查看集群的狀態,我們可以使用mqadmin,命令如下:

./bin/mqadmin clusterlist -n '192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876'
  • clusterlist 是查看集群的命令
  • -n 後面是NameServer的地址,注意這裏也要用”括起來,並且地址之間要用;隔開

執行結果如下:

#Cluster Name     #Broker Name            #BID  #Addr                  #Version                #InTPS(LOAD)       #OutTPS(LOAD) #PCWait(ms) #Hour #SPACE
RocketMQ-Cluster  broker-a                0     192.168.73.130:10911   V4_7_0                   0.00(0,0ms)         0.00(0,0ms)          0 442039.47 -1.0000
RocketMQ-Cluster  broker-a                1     192.168.73.131:11911   V4_7_0                   0.00(0,0ms)         0.00(0,0ms)          0 442039.47 0.2956
RocketMQ-Cluster  broker-b                0     192.168.73.131:10911   V4_7_0                   0.00(0,0ms)         0.00(0,0ms)          0 442039.47 0.2956
RocketMQ-Cluster  broker-b                1     192.168.73.130:11911   V4_7_0                   0.00(0,0ms)         0.00(0,0ms)          0 442039.47 -1.0000

我們可以看到在這個NameServer中心中,只有一個broker集群RocketMQ-Cluster,有兩個broker,broker-abroker-b,而且每一個broker都有主從,broker的ip我們也可以看到。

好了~ 到這裏RocketMQ的集群就搭建好了,有問題評論區留言哦~~

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準

《痞子衡嵌入式半月刊》 第 9 期

痞子衡嵌入式半月刊: 第 9 期

這裏分享嵌入式領域有用有趣的項目/工具以及一些熱點新聞,農曆年分二十四節氣,希望在每個交節之日準時發布一期。

本期刊是開源項目(GitHub: JayHeng/pzh-mcu-bi-weekly),歡迎提交 issue,投稿或推薦你知道的嵌入式那些事兒。

上期回顧 :《痞子衡嵌入式半月刊: 第 8 期》

嘮兩句

今天是芒種,芒種節氣在農耕上有着相當重要的意義,它指導着農事耕種。民諺有“芒種不種,再種無用”一說。

近期總理提出的地攤經濟正席捲全國,這是特殊時期的一種特殊經濟刺激手段。地攤經濟,是最傳統的經濟模式,這意味着人人可以無門檻做生意。還等什麼,痞子衡打算淘寶進一批7天無理由退貨的小玩意去步行街擺攤了。

本期共收錄 3條資訊、3個項目、1個工具、1個RT產品,希望對你有幫助!

資訊類

1、瑞芯微AI芯片加持百度飛槳,攜手加速AI應用落地

瑞芯微Rockchip近日宣布,旗下AI芯片RK1808、RK1806適配百度飛槳(PaddlePaddle)開源深度學習平台,充分兼容飛槳輕量化推理引擎Paddle Lite。此次瑞芯微與百度合作,旨在為AI行業賦能更多應用場景,加速AI產品落地進程。

資訊主頁: https://www.rock-chips.com/a/cn/news/rockchip/2020/0513/1084.html

瑞芯微AI芯片RK1808及RK1806,內置獨立NPU神經計算單元,INT8 算力高達3.0TOPs;採用22nm FD-SOI工藝,相同性能下的功耗相比主流28nm工藝產品降低約30%,在算力、性能、功耗等指標上均有優異的表現。經實測,瑞芯微AI芯片在Paddle Lite中運行MobileNet V1耗時僅為6.5 ms,幀率高達153.8 FPS,二者充分兼容並高效穩定運行。

如上圖所示的實測結果可以看出,與手機等移動端常用的國內外主流CPU相比,RK18系列NPU在MobileNET_v1的耗時更少,表現出色,由此證明在AI相關領域,如圖像分類、目標檢測、語音交互上,專用的AI芯片將帶來更出色的效果。

2、ZLG正式發布AWTK v1.4

近日,ZLG開源GUI引擎AWTK v1.4正式發布。相對於v1.3,新版本中完善了許多細節,增加了部分特性、控件以及API等,同時新增對iOS平台,以及Python、Java、C++等語言的支持。

資訊主頁: https://www.zlg.cn/index/pub/awtk.html

AWTK v1.4新增特性:

- 無文件系統時支持多主題
- OpenGL ES支持snapshot
- edit和mledit支持自己指定的軟鍵盤名稱
- 點擊鼠標右鍵觸發EVT_CONTEXT_MENU事件
- 增加awtk_main.inc,用於標準程序的主函數
- 用SDL重新實現PC版本的線程和同步相關函數 
- edit增加input_type為"custom_password"的類型

AWTK全稱為Toolkit AnyWhere,是ZLG傾心打造的一套基於C語言開發的GUI框架。旨在為用戶提供一個功能強大、高效可靠、簡單易用、可輕鬆做出炫酷效果的GUI引擎,支持跨平台同步開發,一次編程,到處編譯,跨平台使用。 AWTK還配套了所見即所得的AWTK Designer界面設計工具、經典示例以及入門指南文檔等 。

3、Microchip推出軟件開發工具包和神經網絡IP,助力低功耗FPGA智能嵌入式視覺解決方案

隨着人工智能、機器學習和物聯網的興起,對邊緣應用的解決方案提出了更高的需求,例如縮小體積、減少產熱、提高計算性能等。近日,Microchip Technology Inc.(美國微芯科技公司)發布的智能嵌入式視覺解決方案,致力於讓軟件開發人員能夠更方便地在PolarFire®現場可編程門陣列(FPGA)內執行算法,進而滿足邊緣應用對節能型推理功能日益增長的需求。

資訊主頁: http://www.microchip.com.cn/newcommunity/index.php?m=Article&a=show&id=594

VectorBlox加速器軟件開發工具包(SDK)是Microchip嵌入式解決方案組合的重要新成員。受益於FPGA的高運算能力,優秀的能耗比,FPGA成為了邊緣人工智能應用的理想選擇。Microchip的VectorBlox加速器SDK可以幫助開發人員在不學習FPGA工具流或者不具備設計經驗的前提下,利用Microchip PolarFire FPGA創建靈活的低功耗覆蓋神經網絡應用。

項目類

1、EmbedXrpc – 面向單片機的嵌入式小型RPC

EmbedXrpc類似於Google的gRPC,但是應用場景是單片機。RPC遠程調用極大的方便了開發,使得不必關注於協議解析,數據的序列化和反序列化等繁瑣的工作。

項目主頁: https://gitee.com/snikeguo/EmbedXrpc

EmbedXrpc應用場景:單片機近距離Client/Server交互場景(USB、串口、CAN(如J1939 、ISO15765協議等),)只要是流協議都支持。

項目提供了一個Sample1工程,這是最簡單的例子,除了main.cpp的代碼是手工寫的之外,其他的代碼都是工具生成的!此Sample1工程演示了:

1.客戶端每一秒向服務端發送1、2 服務端計算出來3后,把3發送給客戶端
2.服務端每1秒廣播當前的時間,客戶端打印到控制台上

2、m4vgalib – 基於單片機的VGA格式視頻生成庫

m4vgalib庫能使得微控制器(比如STM32F40x/1x)輸出高質量、高分辨率彩色圖形,並且這個庫使用很少的外部組件。

項目主頁: https://github.com/cbiffle/m4vgalib

該庫示例單片機STM32F407是一個Cortex-M4微控制器,它既沒有視頻控制器,也沒有足夠的RAM用於任何合理分辨率的幀緩衝區。m4vgalib圍繞這一點工作,生成穩定的800×600(或640×480)256色視頻。m4vgalib不使用視頻控制器,而是使用兩個定時器、一個DMA控制器和一個GPIO端口。

儘管m4vgalib在一個不是為任何類型設計的處理器上維護320Mb/s的數據流,但是大多數CPU和硬件資源都留給應用程序使用。為了避免引入抖動,應用程序必須同意在執行的某些階段避開AHB1。(比如可以使用中斷來通知應用程序。)

3、cmd-parser – 一個非常簡單好用的命令解析器

cmd-parser是一個非常簡單好用的命令解析器,佔用資源極少極少,採用哈希算法超快匹配命令。

項目主頁: https://github.com/jiejieTop/cmd-parser

簡單來說,如果你希望你的開發板,可以通過命令執行一些處理,比如說用串口發一個命令A,開發板就執行A的一些處理,或者,在調試某些AT模組的時候,當收到模組返回的一些指令后,自動執行一些處理。當然,還有其他的地方可以用得上的,大家可以自行挖掘!

cmd-parser特點如下:

1. 用戶無需關心命令的存儲區域與大小,由編譯器靜態分配。
2. 加入哈希算法超快速匹配命令,時間複雜度從O(n*m)變為O(n)。
3. 命令支持忽略大小寫。
4. 非常易用與非常簡潔的代碼(不足150行)。

工具類

1、SpeedCrunch – 高精度科學計算器

SpeedCrunch是一款開源的高精度科學計算器,具有快速,鍵盤驅動的用戶界面。

軟件主頁: https://github.com/speedcrunch/SpeedCrunch

SpeedCrunch內置80多個數學函數,允許用戶使用複數,数字基數,單位轉換等執行最高50位精度的計算,其自動完成功能可加快工作速度,提升效率。SpeedCrunch還內置公式簿,可方便用戶查看和插入常用的公式,例如圓錐體的體積計算公式等。

i.MXRT出品

1、魚躍醫療 – 高流量呼吸濕化治療儀

這款高流量呼吸濕化治療儀所支持的經鼻高流量氧療(HFNC)是一項新型的氧療方式,已被國內外大量臨床研究證實在缺氧改善治療中有很高的應用價值。HFNC通過柔軟的鼻塞導管將最高達75L/min流量的空氧混合氣體,經由加溫加濕后輸送給患者。

RT芯片:i.MXRT1052
產品主頁: https://www.yuyue.com.cn/index.php/news/info/795.html
參考售價: 未知

歡迎訂閱

文章會同時發布到我的 博客園主頁、CSDN主頁、微信公眾號 平台上。

微信搜索”痞子衡嵌入式“或者掃描下面二維碼,就可以在手機上第一時間看了哦。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

台中搬家公司費用怎麼算?

19萬選中級車 敢選這幾輛說明你品位不是一般的高!

而在內飾上雪鐵龍C6的用料豪華程度也着實讓人驚嘆,大量的nappa真皮使用給車內營造了不錯的檔次感。內飾整體的設計比較的平直。在法系車的先產品中屬於十分保守的設計風格。這也比較能夠符合雪鐵龍C6中級車的特質。而且這樣的設計也比較的耐看。

如今中國消費者對於SUV的喜愛不用我過多的贅述。但是看乘用車的銷量分佈情況來看轎車賣的還是比SUV多。有那麼一些人對於高大空間靈活但是笨重耗油的SUV沒有什麼興趣,而對空間寬敞外觀體面的中級轎車有着特別的偏愛。

說到中級車很多人會想到商務中庸。但是並不是所有的中級車都是這樣。比如說下面這幾台中級車開出去你就足以彰顯你的獨特品位。

大眾邁騰

指導價:18.99-31.69萬

大眾2017款邁騰相對於老邁騰那個商務的外觀來說可以說年輕了十歲,甚至有些像大眾CC的感覺了。更加舒展的側面更加犀利的前臉都使得大眾新邁騰在外觀上更加具有設計感。而複雜的燈腔結構、大量的鍍鉻裝飾條的使用以及橫平豎直的直線條都讓這款新邁騰的外觀呈現出了不錯的精緻感。

而在內飾上新邁騰也是用了大眾最新的設計風格。空調出風口的造型設計尤其獨特。鋼琴烤漆的中控面板、精緻的儀錶盤造型、擋把區域的按鈕排布設計都使得新邁騰的設計在同級別里出類拔萃,十分的有設計感以及質感。加上優秀的內飾做工,新邁騰的內飾真的堪比豪華轎車。

新邁騰的入門車型為280TSI DSG舒適型。指導價18.99萬,1.4T發動機最大功率150馬力最大扭矩250牛米。搭配七擋雙離合變速箱。整體動力表現還是很不錯的。

雪鐵龍C6

指導價:18.99-27.99萬

說到雪鐵龍C6可能大家會比較陌生一點。C6最初定位於中大型豪華轎車。但是在這個看品牌的時代,雪鐵龍的品牌顯然不足以與BBA等豪車競爭。因此雪鐵龍C6也自降身價。雪鐵龍新C6定位於中級車,但是。無論是外觀的設計感豪華感精緻感。雪鐵龍C6都不遜色於入門級豪華轎車。

而在內飾上雪鐵龍C6的用料豪華程度也着實讓人驚嘆,大量的nappa真皮使用給車內營造了不錯的檔次感。內飾整體的設計比較的平直。在法系車的先產品中屬於十分保守的設計風格。這也比較能夠符合雪鐵龍C6中級車的特質。而且這樣的設計也比較的耐看。值得一提的是擋把區域的設計感比較強。但是空調出風口的位置太靠下,使用起來不方便。

19萬隻能買到雪鐵龍C6的1.6T+6AT的車型,但是這台1.6T發動機也完全足夠它的動力。沒錯,這台1.6T就是小編美美非常喜歡的雪鐵龍1.6THp發動機。匹配6AT變速箱,整個動力系統的線性程度十分讓人滿意,高級感非常好。

馬自達阿特茲

指導價:17.58-23.98萬

馬自達阿特茲這款車我們大家都不陌生,它轎跑式的造型設計風格,碩大的輪轂以及凌厲的前臉設計都讓人印象深刻,說它是一款轎車中最有跑車風格的車,我相信沒有人會反對。如果你是一個外觀控的話那麼你一定要看一看阿特茲。

如果說老款阿特茲的中控台讓我們詬病,成為了阿特茲最大的短板,那麼改款之後的2017款阿特茲則完全不存在這樣的問題。改款之後的內飾設計不僅和丑字撇開了關係,而且十分的具有設計感。這個內飾終於能夠和外觀站在同一水平線上了。

19萬不到的價錢當然只能買到一台2.0升的阿特茲。但是這台2.0升的發動機已經能夠提供給你不錯的動力響應和駕駛感受。2.0升自然吸氣發動機,最大功率158馬力,最大扭矩202牛米,搭配六擋手自一體變速箱,這套動力系統比較的常規,但是實際體驗還是不錯的。

精緻的邁騰,用料高檔的雪鐵龍C6,造型時尚的馬自達阿特茲。這三款車型可以說是目前中級車市場中的三個清新脫俗的選擇,他們都有着不錯的外觀設計,性價比也足夠讓人滿意,想拒絕平庸?這些特點鮮明的車型完全足以彰顯你的獨特。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

台中搬家公司費用怎麼算?

月銷過萬才靠譜!10萬買SUV我只考慮這幾輛!

長安CS35指導價:7。89-9。89萬優惠區間:6000元左右推薦車型:2016款 1。6L 自動尊貴型 國V10月份銷量:17583台長安CS35作為長安汽車自主研發的首款SUV,外觀造型挺拔有力,前大燈採用刀削式設計風格,外形同樣非常飽滿,中庸色彩偏重,內飾上長安CS35各部分覆蓋件的拼接都比較到位,全黑內飾的搭配下,只能說低調實用風格,並沒有太多華麗的飾條。

SUV一直是國人的熱愛車型,有着能隨手放嬰兒車的空間,有着能隨意走爛路的高底盤,有着比轎車更開闊的駕駛視野,但與此同時,也有着比轎車更高的油耗表現,如今的SUV車型已經日漸走入“微時代”的大環境,許多喜歡SUV的消費者們,購買小型SUV,是他們的第二選擇,一來價格相對便宜,二來車身較輕油耗也相對較低,所以從這幾天剛發布的中國SUV銷量排行榜(10月版)中可以看到,在小型SUV的銷量排行上,月銷過萬的熱門車型,大部分都屬於我國自主品牌的產品。

長城哈弗H2

指導價: 8.68-12.88萬

優惠幅度:2000元左右

推薦車型:2017款 紅標 1.5T 自動兩驅豪華型

10月份銷量:21079台

哈弗H2作為長城汽車旗下哈弗推出的一款小型SUV,外觀上採用全新的哈弗家族臉譜,大面積鍍鉻進氣格柵設計,整體車頭飽滿大氣,外形中庸(這點最符合國人的審美),內飾上哈弗H2比以往車型更加註重材料的搭配,車內增加了大量的金屬質感裝飾以及高光的木質裝飾,在形象上高檔了不少。

推薦的這款紅標1.5T自動兩驅豪華型(指導價11.18萬元)上,配置確實沒得說,安全方面配備了8個安全氣囊,胎壓監測裝置,ESp車身穩定控制,上坡輔助,自動駐車,其他方面也有着電動天窗,自動空調,定速巡航,倒車雷達及影像,無鑰匙進入與啟動等等配置,確實應該有的實用配置都有了。

在動力上1.5T發動機最大功率為150匹,峰值扭矩為210牛米,搭配6擋手動和6擋手自一體變速器,手動車型同時還有適時四驅系統的介入,懸架上前麥弗遜獨立懸架,后多連桿獨立懸架,在過彎提供更好地運動支撐性,這款小型SUV確實不簡單。

江淮瑞風S3

指導價:6.58-9.58萬

優惠區間:5000元左右

推薦車型:2017款 1.6L CVT 智能互聯型

10月份銷量:19659台

瑞風S3前臉沿用江淮“寶瓶口”設計,融合了都市跨界SUV的設計元素,呈現出一派時尚與活力,進氣格柵加上三根顯著的鍍鉻條,全新的車尾設計,拉伸了視覺寬度,內飾上整體選用深色內飾,配合紅色縫合線,儀錶採用雙炮筒設計,更有運動感。

所推薦的這款2017款 1.6L CVT 智能互聯型(指導價:9.58萬元),雖然在動力上並沒有哈弗H2的1.5T那麼強勁,但是同樣豐富的配置下,裸車價10萬還不到,想要買來家用代步的話,1.6L的動力是沒有問題的,而且整車質量只有1275KG,相當於一輛普通的家用轎車的重量,205/55 R16的薄輪胎規格,減少與地面的摩擦力,再加上無級變速箱的傳輸,無疑地,這台瑞風S3能夠將油耗降到很低。

在試駕感受上,1.6L自然吸氣發動機搭配CVT無級變速器並不會有太大的激情,前段油門調校比較敏感,在城市中50km/h以下的加速能力一點也費勁,方向盤轉向輕盈有點虛,底盤相對來說還是比較厚重。

長安CS35

指導價:7.89-9.89萬

優惠區間:6000元左右

推薦車型:2016款 1.6L 自動尊貴型 國V

10月份銷量:17583台

長安CS35作為長安汽車自主研發的首款SUV,外觀造型挺拔有力,前大燈採用刀削式設計風格,外形同樣非常飽滿,中庸色彩偏重,內飾上長安CS35各部分覆蓋件的拼接都比較到位,全黑內飾的搭配下,只能說低調實用風格,並沒有太多華麗的飾條。

推薦的這款2016款 1.6L 自動尊貴型 國V(指導價:9.89萬),在配置上,如無鑰匙進入與啟動,ESp車身穩定系統,電動天窗,倒車影像及雷達,后視鏡自動摺疊皆有配備,每逢在說這些配置的時候,真的感覺10萬元如果將眼光對準自主品牌,確實要啥配置都有啥配置,不需要愁。

動力上有着1.5T渦輪增壓發動機,最大功率156匹馬力,峰值扭矩215牛米,雖然動力不錯,但是變速箱只匹配5擋手動,這對於追求大動力卻想買個自動變速器的消費者來說,這就有點可惜了,另一方面還有着1.6L自然吸氣發動機,動力雖然較弱但也足夠代步,且同時匹配5擋手動和4擋手自一體變速器。

奇瑞瑞虎3

指導價:6.89-9.29萬

優惠區間:暫時本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

你有十天的時間,來感受一台有格調又運動的寶馬汽車

富有激情的操控樂趣BMW同樣是一個及其注重汽車運動性調校的品牌,BMW 1系轎車同樣也不例外,BMW 1系帶有驅動力控底盤技術,經過BMW工程師標誌性的運動化調校,BMW 1系有着跟隨性優異,支撐力出色的底盤表現,配合上搭載了技術領先、動力強勁、響應迅速的BMW最新一代B系列渦輪增壓發動機,以及回饋精準迅速的單齒电子助力轉向系統,BMW 1 系依然保留了純正的BMW招牌式運動基因。

雖然說現在中國汽車消費群體已經越來越趨於年輕化,但是年輕人購車還是有許多的煩惱,不少人積攢了好久的積蓄購買了人生第一輛汽車,無奈遇上操控不舒適、動力太孱弱、亦或是發現其實自己辛苦買來的車型有着很多不常用甚至從來不用的配置。

而在今年廣州車展正式亮相的全新一代BMW1系三廂轎車,就是一款綜合了趣味十足的操控、奢華的品牌格調、舒適的家庭氛圍的高顏值運動家轎,勢必會成為年輕一代購車人群的全新選擇。

富有情懷的品牌

情懷這個形容詞並不是所有汽車品牌都可以擁有;BMW公司成立於1916年,從最初的飛機製造廠到現在的世界知名豪華汽車品牌,時光與歲月所給予的百年積澱,成就了一個血液里充斥着年輕、活力、運動基因的豪華汽車品牌;那醒目的藍白標誌儘管十分簡潔,但無一不在訴說著它與生俱來的高檔情懷。

富有個性的高顏值外觀

BMW 1系三廂轎車外觀設計靈感來自於Compact Sedan概念車,是一台為獨立自我的年輕人打造的運動性最強的緊湊型轎車。修長的發動機蓋與雙門轎跑車似的平滑車頂線條,使全新BMW 1系轎車視覺感官十分動感緊湊,經典的BMW車身比例讓全新BMW 1系的視覺感受更加的協調。

發動機蓋與後備箱隆起的肌肉線條更是凸顯了一種運動范兒十足的橫向張力,頭燈和尾燈的設計沿襲了BMW經典元素,車頭的“光暈環”輔以當下BMW標誌性的“開眼角”燈眉設計,採用LED光源作為車頭前大燈和日間行車燈的配置,讓BMW1系的大燈犹如以犀利的眼神凝視前方,L型尾燈則用充滿設計感的立體折線作為呼應,是與時尚達人完美匹配的炫酷裝備。

富有激情的操控樂趣

BMW同樣是一個及其注重汽車運動性調校的品牌,BMW 1系轎車同樣也不例外,BMW 1系帶有驅動力控底盤技術,經過BMW工程師標誌性的運動化調校,BMW 1系有着跟隨性優異,支撐力出色的底盤表現,配合上搭載了技術領先、動力強勁、響應迅速的BMW最新一代B系列渦輪增壓發動機,以及回饋精準迅速的單齒电子助力轉向系統,BMW 1 系依然保留了純正的BMW招牌式運動基因。

富有創新的全新互聯科技

內飾設計同樣彰顯着運動美學,整體向駕駛者傾斜的中控台是典型的跑車化設計元素,加之包裹性和舒適度都十分優良的運動座椅,多功能運動方向盤,以及以駕駛者為中心的內飾布局方式更增加了BMW 1系駕駛員在駕控過程中增添了戰鬥氣息。

時代的進步離不開科技,汽車作為當下社會高科技產物的一個載體,先進的科技裝備是必不可少的配置,標誌性的BMW手寫功能iDrive系統便捷快速,同級獨有的HUD抬頭显示系統讓駕駛更加安全與專註,同級獨有的互聯駕駛應用商店和駕駛系統融入在8.8寸的显示屏中,智能互聯,旅程諮詢、緊急救助等功能全然囊括,真正意義上做到智能、安全、放心的出行。

BMW的品牌奠定了它的格調,BMW與生俱來的運動血液賦予了BMW 1系運動轎車優秀的操控性和卓越的性能表現,定位親民,布局實用的特性使得BMW 1系同樣擁有着生活的溫馨與情趣。

BMW作為一個富有運動血液的品牌,本次為中國年輕一代消費者所推出的全新BMW1系運動轎車,是啟動人生旅程,助力未來創造更具有激情的生活的優秀夥伴。憑藉著時尚動感的外表,激情澎湃的內“芯”,以及潮流炫酷的科技,BMW1系作為最新潮的BMW汽車,已經在2016年度第14屆廣州國際車展上亮相,明年年初BMW 1系將正式公布價格並且上市銷售;11月18日至27日,歡迎光臨2016廣州國際車展4.1號館BMW展台,一睹全新BMW 1系運動轎車的年輕魅力。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※推薦台中搬家公司優質服務,可到府估價

國外測試 macOS Big Sur 使用 Chrome 時,RAM 記憶體使用量是 Safari 的 10 倍以上

Google Chrome 非常吃記憶體這是大家都知道的事情,即便 Chrome 87 版本已經有所改善,但相較於其他瀏覽器,佔用率還是高出不少,最近國外就有人實測 macOS 最新 Big Sur 版本的 Safari vs Chrome 比較,不比還好,一比沒想到竟然相差 10 倍以上,而且是分頁數量越開越多,差距也越來越大,這也代表說 Mac 用戶瀏覽器最好還是使用 Safari。

macOS Big Sur 使用 Chrome 時,RAM 記憶體使用量是 Safari 的 10 倍以上

最近一位 Flotato 的開發者 Morten Just,在官網上分享一篇 “瀏覽器吃掉 RAM” 的文章,他在 macOS 最新系統版本中,實驗兩種情況,來測試 Safari 與 Chrome 佔用多少記憶體。

首先是打開 Twitter.com 網站後,瀏覽一下,然後再開啟 Gmail 電子郵件服務。從下圖可以看到,光是僅開啟兩個網頁分頁,Chrome 最高峰就已經佔用 1GB RAM:

Safari 則保持在 80MB 以下。另外這測試是在 VM 虛擬機上運行

平均來看,Chrome 使用約 730MB,Safari 只有 73MB,差距達到 10 倍:

第二項則是壓力測試,他在 2019 年 MacBook Pro  16 上運行,共打開 54 個網頁分頁,Chrome 斜線快速上升,幾分鐘內 RAM 佔用率就達到了 1500MB:

Safari 依舊非常穩定,從這表格來看低到根本看不出使用多少 RAM:

而換算每個分頁使用的 RAM,Chrome 約 290MB,Safari 只有 12MB,意味著差距來到了 24 倍:

雖然這次實驗主要是為了推廣 Flotato 軟體,但他也提到 Chrome 的 RAM 佔用率比他想像還多,不過他確信 Chrome 還是會有效管理每個分頁佔用 RAM 的情況,進而確保當前網頁瀏覽順暢。

另外當開啟許多應用程式時,如:Sketch、Final Cut、Photoshop,Mac 也會嚴格分配 Chrome 的 RAM 使用率,因此實際使用可能也不會那麼多。

不過可以確定的是,如果你不希望瀏覽器消耗掉你太多 RAM,Safari 絕對是 Mac 系統的最佳選擇。

資料來源:flotato

Chrome 變萌了,教你如何製作小恐龍 QR Code 分享網址

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

Google Pixel 6 傳臉部辨識將回歸,且還會具備螢幕下指紋解鎖

離今年 Google Pixel 6 可能發表的時間雖然還有超過半年,但已經開始有一些傳言現身,而且這一個可說是很多 Pixel 用戶都會高興的消息。最近國外開發者從 App 程式碼中發現,Pixel 5 被取消的臉部辨識技術,Pixel 6 很有機會回歸,而且還會再加入螢幕下指紋解鎖功能,等於說生物辨識技術部分跟 iPhone 13 目前傳聞一樣。

Google Pixel 6 臉部辨識將回歸?

近日一位國外知名 XDA-Developers 開發者 Mishaal Raman 在 Android 12 開發版的設定 App 中,找到關於 “用指紋和臉部安全解鎖手機”、”於口袋中節省時間解鎖手機”、以及 ”輕鬆快速解鎖手機” 的程式碼,這也暗示著,今年 Google Pixel 6 可能臉部、指紋解鎖都有,一次滿足各種使用需求:

So the Settings app is preparing to support devices with both facial auth and fingerprint scanners. Not much else on this, so I wouldn’t read too much into it yet. pic.twitter.com/xzPWd5cV2m

— Mishaal Rahman (@MishaalRahman) February 18, 2021

臉部辨識技術早在 Pixel 4 時代就已經有,不過 Pixel 5 不知為何取消,改回指紋辨識,有人猜測是成本考量,這也是為什麼 Pixel 5 能賣這麼便宜的其中一個原因(處理器為主因)。

另外程式碼中並沒有提到是採用螢幕下指紋辨識技術,不過現在國外媒體都認為是這一個。

Pixel 5,開箱文章請點我閱讀:

話說回來,原本以為 Pixel 5 降價,未來 Pixel 系列就會走這路線,不跟其他大品牌的旗艦手機拼硬體規格,但隨著這傳言出現,搞不好 Pixel 6 也會再度變回真正的旗艦手機,對於硬體至上的 Pixel 用戶來說,可以期待一下。

至於其他,目前就沒有相關消息,根據外媒 Tom’s Guide 的整理,處理器有可能是 Snapdragon 888 或 Snapdragon 870 5G,就看 Pixel 6 的售價而定。電池可能會變更大,顯示器可以期待提升到 120Hz,沒意外應該會有 Pixel 6 與 Pixel 6 XL 兩種選擇,預計今年 10 月初推出。

資料來源:Notebookcheck

Google 公布 12 月更新內容,多個 Pixel 5 特色功能都下放到 Pixel 3 以上的舊機種

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

※回頭車貨運收費標準

重量不到一公斤!結合高效能與超輕薄全新筆電 ASUS ZenBook 14 Ultralight (UX435EGL) 開箱

為大家介紹結合高效能與超輕薄全新筆電 ASUS ZenBook 14 Ultralight (UX435EGL) 開箱。輕薄筆電,相信這對於很多人來說是又愛又恨,之前也是有許多人在問小編給予筆電推薦時,都會提出想要能夠有高效能,但又想要是輕薄好攜帶的需求,尤其是女生的首選,幾乎都是要求輕薄筆電為優先的情況下(也比較好看才是真的),那麼就很難去好好推薦,要有效能的輕薄筆電幾乎都是要到高階商務人士用的,但通常到了那種階級的筆電,幾乎都是超出預算,但即使超出預算了,效能還是沒辦法達標,因此輕薄筆電的選擇一直都是一個難題。

不過在今年配合著Intel第11代移動處理器的發布,似乎這個難題有了許多的解法,而華碩在今年年初推出的ZenBook 14 Ultralight (UX435EGL),讓小編對於高效輕薄筆電的推薦選項更為集中了,除了擁有低於1公斤的超輕便重量外,同時擁有了非常實用的運算能力,以及能夠滿足繪圖或輕度影片剪輯的效能,搭配最高17小時的超長續航力、完整的I/O配置等等,可以說是非常值得推薦的一台輕薄筆電,就讓我們一起來看看這台全新的 ASUS ZenBook 14 Ultralight (UX435EGL) 吧。

ASUS ZenBook 14 Ultralight (UX435EGL) 開箱!!

ZenBook 14 Ultralight 的上蓋採用了金屬磨砂材質,唯一配色的綠松灰設計,在時尚中又帶點了年輕的元素。

掀開螢幕後,映入眼簾的是四面NanoEdge極窄邊框的14吋Full HD廣視角螢幕,螢幕佔比高達了92%,讓螢幕看起來更大了,幾乎沒有存在感的黑邊,有著絕佳的沉浸體驗。

而且即使是在NanoEdge極窄邊框的上邊,還是保有紅外線攝影機,因此可以非常輕鬆的使用Windows Hello人臉辨識進行登入。此外,在現今全球疫情尚未趨緩的時期,對於遠端工作者或是有線上會議需求,有紅外線攝影機可進行遠距的視訊會議也非常便利!

在螢幕的底部,也精心打造了ErgoLift 螢幕軸承設計,在掀開螢幕的同時會自動將鍵盤傾斜至最舒適的位置。

鍵盤的部分採用全尺寸設計,並且在最右邊與最上邊還設有許多功能鍵方便使用。

1.4mm的鍵程,搭配非常滑順的鍵帽,在打字上也可以非常舒服。

上方的功能鍵,預設就是調整功能,不用另外搭配Fn鍵才能使用,但如果需要切換回傳統的組合方式,可以透過Fn+Esc做切換,在傳統模式下Fn按鍵會做發光提示。

觸控板部分,除了支援四指智慧手勢外,點擊右上角icon就可以開啟NumberPad虛擬數字鍵盤,讓輕薄的14吋筆電也有完整的數字鍵可以使用,且開啟的同時仍可控制游標。

在I/O配置部分,即使在超輕薄的設計下,還是保有了許多方便的接口,左側留有HDMI 2.0、兩個Thunderbolt™ 4 USB-C®以及電源指示燈。

在右側的部份,則是3.5mm 耳麥組合接口以及一個USB 3.2 Type A (Gen 1),保留一個Type A的接口讓使用起來更加的便利,另外還有一個micro SD讀卡機,讓繪圖、剪輯可以更方便的讀取和儲存檔案。

翻到背面,首先可以看到配合著ErgoLift 螢幕軸承的墊高,讓散熱效能更為強悍的開孔。

在背面的左右兩側,則是兩個針對喇叭的開孔,ZenBook 14 Ultralight 的音訊裝置通過了專業音響大廠Harman Kardon的認證,因此可以有著絕佳的聽覺體驗。

接著我們準備進行拆機,不過要特別注意的是ZenBook 14 Ultralight有兩顆螺絲藏在了止滑墊的下方,因此要特別注意。

開蓋後,可以看到在非常輕巧的配置下,內部的空間確實也盡量的緊縮,因此包含了記憶體、無線網卡都是直接集成在了主機板上。

ZenBook 14 Ultralight出廠已經內建1TB的PCIe® NVMe™ 3.0 x4 M.2 SSD,相信是非常夠用的。

散熱部分則是使用了單風扇設計,透過石磨片串聯了GPU與CPU,將兩者產生的廢熱透過特殊散熱鰭片排出,讓筆電能夠發揮出高效能。

電池部分則是採用了63Wh,最高續航力官方給出了最高17小時*,可以說是非常的長效。*官方數據續航測試特定條件請參考官網說明:http://bit.ly/2JLh4ea實際電池續航力依據產品組態、使用情形、操作條件及電源管理設定而有有所不同。

而充電器的部分,採用65W的Type C充電器,支援PD快充,可以在50分鐘內就充滿了60%的電力。

最後,ZenBook 14 Ultralight 的名稱由來,就是由於其不到1公斤的重量(官方數據為995克),在如此輕薄的重量,機身最薄僅有14.9mm的機身下,我們一起來看看到底可以發揮出多大的效能吧!

了解更多 ASUS ZenBook 14 Ultralight (請點我)

 

ASUS ZenBook 14 Ultralight 效能測試

本次測試機台是搭載了全新第11代 Intel® Core™ i7-1165G7 處理器與NVIDIA® GeForce® MX450獨立顯示卡的ZenBook 14 Ultralight (UX435EGL)。

透過裝置管理員,可以看到 Intel® Core™ i7-1165G7 4C/8T的配置。

同樣透過裝置管理員,也可以看到ZenBook 14 Ultralight (UX435EGL) 另外搭載的NVIDIA® GeForce® MX450獨立顯卡。

ZenBook 14 Ultralight 出廠就搭配了16GB的記憶體。

首先我們以CPU-Z進行核心效能測試,單核的分數為571.4,而在全核心的分數則是跑到2627.7。

接著透過Cinebench R15同樣進行CPU測試,在CPU單核的分數為219 cb,而全核心的表現則為822 cb。

另外透過了CineBench R20進行CPU測試,在CPU單核的分數為557 cb,而全核心的表現則為2158 cb。

接著透過x264 FHD BENCHMARK進行轉檔測試,i7-1165G7的表現為28 fps。

接著透過x265 FHD BENCHMARK進行轉檔測試,i7-1165G7則是18.7 fps。

在儲存的部分,首先使用DiskInfo查看硬碟資訊,本次測試機台預設是搭載Intel的1TB PCIe® NVMe™ 3.0 x4 M.2 SSD。

透過CrystalDiskMark進行測試,其讀寫速度為1900.9 MB/s與1754.5 MB/s。

接著使用TxBench進行讀寫測試,最高讀寫速度為1868.1 MB/s與1800.9 MB/s。

而對於顯示晶片的測試部分,由於ZenBook 14 Ultralight (UX435EGL) 同時具有全新的Intel® Iris® Xe 顯示晶片以及NVIDIA® GeForce® MX450獨立顯卡,因此針對顯卡測試的3DMARK,使用Time Spy測試,將NVIDIA® GeForce® MX450設為主要測試對象,分數則為1854。

而針對大部分遊戲,使用DirectX 11為核心架構的Fire Strike測試,ZenBook 14 Ultralight的分數為4174。

接著是跨平台的Wild Life測試,ZenBook 14 Ultralight 的分數為11123。

最後則是採用Direct X 12的Night Raid測試,ZenBook 14 Ultralight的分數為17419,由以上幾個測試來看,ZenBook 14 Ultralight即使是在不到1公斤的重量下,還是可以有著還不錯的遊戲效能,實在非常強悍。

整機部分則使用PCMark 10進行測試,在針對辦公室PC的基準測試當中跑分為5137。

而針對完整的辦公室PC基準測試PCMark 10 Express中,跑分為4665。

了解更多 ASUS ZenBook 14 Ultralight (請點我)

 

ASUS ZenBook 14 Ultralight 軟體介紹

在軟體的部分,ZenBook 14 Ultralight主要的功能設定都可以在MyASUS裡面進行,點開軟體即可看到筆電的專屬資訊。

在第二頁則是客戶服務部分,如果電腦有任何問題都可以透過這裡進行與官方的聯繫。

如果是已經知道問題的所在,也可以透過MyASUS直接進行系統診斷,透過軟體分析直接發現電腦問題所在。

無論是軟體、驅動、韌體等等的更新部分,也可以直接透過MyASUS進行設定。

而在硬體設定的部分,可以調整的部分就非常多樣,首先是電池的模式,可以是長效使用、平衡使用或是最佳使用,都可以依照個人習慣做調整。

接著是對於風扇的模式進行調整,這裡主要是針對效能設定的部分,如果要電腦高效能運算的話,直接開啟高效能模式就對了,只有這樣才能發揮出ZenBook 14 Ultralight 的最高效能。

在螢幕色彩的調整部分,則是可以透過Splendid進行螢幕的微調,調整到自己喜歡的顏色。

而開啟Tru2Life則是可以直接透過智慧演算法,讓螢幕呈現出一個最佳的狀態。

而功能鍵鎖定的部分,則是可以自訂最上方的功能鍵預設是要使用原本的功能還是ZenBook 14 Ultralight特定的功能按鍵。

WiFi SmartConnect顧名思義即是自動連線到訊號最好的無線網路。

而麥克風的部分,由於疫情的關係,使得遠端居家辦公的模式越來越盛行,因此針對收音ZenBook 14 Ultralight 搭配了絕佳的ASUS AI降噪技術,可以直接透過AI智慧進行背景音降噪,因此在多人會議當中,即使是在吵雜的環境下也可以保有非常清晰的線上會議體驗。

而在ClearVoice同樣也是透過了AI智慧運算,可以將筆電喇叭中,減少人聲外的所有聲音。

影體設定的最後一項則是TaskFirst優先網路分配,可以針對不同的使用情境去做網路頻寬使用的優化。

硬體設定完畢後,最後則是可以透過MyASUS與智慧型手機做連接,可以透過Link to MyASUS直接用電腦撥打電話、回覆訊息、同步畫面、螢幕延伸等等,非常的方便。

無論是iOS還是Android的用戶,都可以使用喔!

了解更多 ASUS ZenBook 14 Ultralight (請點我)

總結

ZenBook 14 Ultralight (UX435EGL) 可以說是一台要效能有效能、要輕便有輕便的一台高效能筆電,從Intel發表了第11代移動處理器開始,輕薄筆電的效能整個大躍進,而ZenBook 14 Ultralight (UX435EGL) 在此基礎上,更上一層樓,另外搭載了NVIDIA® GeForce® MX450獨立顯卡,讓輕薄筆電也有了更優異的運算能力,在不到一公斤的機身內你會發現令你想不到的效能爆發,並且還有非常完整的I/O配置以及非常長效的續航力,因此無論是在商務運用、設計運算等等,同時兼具了高效能、高續航、高便攜,在這之前是非常難想像一台筆電就能勝任大部分的工作,但在ZenBook 14 Ultralight (UX435EGL) 隔空出世之後,真的是刷新了小編對於輕薄筆電的印象,非常推薦給大家。

 

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準