std::set(vs2010)のメモリ使用量

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
やま

std::set(vs2010)のメモリ使用量

#1

投稿記事 by やま » 9年前

<環境>
OS win7 64bit
コンパイラ vs2010,Releaseビルド(exeをダブルクリックで起動).
バイト境界 4

<したいこと・問題点>
std::setを使用した際のメモリ使用量を求めようとしております.
しかし,vs2010のstd::setのメモリ使用量の計算値と
タスクマネージャー(又はProcessExplore)のプライベートワーキングメモリが整合しません.
OSが気を効かせてくれたにしては,少し多すぎるような気がしております.

<実験方法>
setに1000000個の構造体を入れて確認してみました.
set制御用のメモリが12bytes,挿入した構造体はPadding含め8bytes(int,char)と12bytes(char int char)で,
1要素のTotalはそれぞれ,20bytes/24bytesです.
//アロケータで確認済.

本来ならば,
1000000 * 20 = 20000000 = 19531.25 kbytes
1000000 * 24 = 24000000 = 27343.75 kbytes
となるはずです.

しかし,Windowsタスクマネージャー(ProcessExploreでも同様)で確認したところ,
それぞれ
20bytes -> 31372 K
24bytes -> 39220 K
となり,明らかに違いが生まれました.

この違いは一体どこからくるものなのでしょうか?

<コード>

コード:

//Setの要素のはず.xtree.hの中にあったものを少し変形.
//struct Node{
//Node* _Left;	    //左へのポインタ    (4byte)
//Node* _Parent;    //親へのポインタ    (4byte)
//Node* _Right;	    //右へのポインタ    (4byte)
//value_type _Myval;//setに格納される値 (xbyte)
//char _Color;	    //赤黒            (1byte) //
//char _Isnil;	    //headの時true    (1byte) //(2:char 2:padding)
//};
// total : 4+4+4+ x +4 = 12+x (bytes)


using namespace std;

//メモリ確保量確認用アロケータ
template<class T>
class myallocator : public std::allocator<T> {
public:
    myallocator() { }
    myallocator(const myallocator& x) { }
    
    template<class U>
    myallocator(const myallocator<U>& x) { }
    
    pointer allocate(size_type n, const_pointer hint = 0) { 
        std::cout << "allocate " << n * sizeof(T) << "bytes" << std::endl;
        return (pointer) std::malloc(n * sizeof(T));
    }
    
    void deallocate(pointer ptr, size_type n) {
        std::cout << "free pointer " << (void*) ptr << std::endl;
        std::free(ptr);
    }
    
    template<class U>
    struct rebind { typedef myallocator<U> other; };
};


struct test1{
    char a; // 4 (1:char 3:padding)
    int b; // 4 
}; //8bytes -> total 12+8 = 20bytes

struct test2{
    char a; // 4( 1:char 3:padding)
    int b; //4
    char c;// 4( 1:char 3:padding)
}; //12bytes -> total 12+12 = 24bytes


struct Test1LessThan : less<struct test1> {
    bool operator()( struct test1 a, struct test1 b ) { return a.b > b.b; }
};

struct Test2LessThan : less<struct test2>{
    bool operator()( struct test2 a, struct test2 b ) { return a.b > b.b; }
};

void test1(){
     set< struct test1,Test1LessThan, myallocator<struct test1> > s;
     for(int i=0;i<1000000;i++){
     	test1 t;
	t.b=i;
        s.insert(t);
    }
}

void test2(){
    set< struct test2,Test2LessThan, myallocator<struct test2> > s;
    for(int i=0;i<1000000;i++){
        test2 t;
	t.b=i;
	s.insert(t);
    }
}


//バイト境界確認
template <typename T>
class alignof{
    struct helpler{
        char a_;
        T b_;
    };
public:
    static const size_t value = offsetof( helper, b_);
};

int main(){

    int a;

    cout << alignof<test1>::value << endl; //バイト境界は4

    cin >> a;//とめる
    test1(); // 計算通りなら,1000000*20bytes = 19.07MB

    cin >> a;//とめる	
    test2(); // 計算通りなら,1000000*28bytes = 26.7MB
	
    return 0;
}

やま

Re: std::set(vs2010)のメモリ使用量

#2

投稿記事 by やま » 9年前

コードに一部誤りがありました.
//確認するために標準入力をはさみ一時止める.
ついでに,構造体と関数名が同じなので一応変更…
本質とは無関係と思いますが念のため訂正いたします.

コード:

void func_test1(){
    int num;

   cin >> num; //入った直後にとめる.

    set< struct test1,Test1LessThan, myallocator<struct test1> > s;
    for(int i=0;i<1000000;i++){
        test1 t;
        t.b = i;
        s.insert(t);
    }

    cin >> num; //抜ける前にとめる.
}
 
void func_test2(){
    int num;

    cin >> num; //抜ける前にとめる.

    set< struct test2,Test2LessThan, myallocator<struct test2> > s;
    for(int i=0;i<1000000;i++){
        test2 t;
        t.b = i;
        s.insert(t);
    }

    cin >> num; //抜ける前にとめる.
}

int main(){
  
    cout << alignof<test1>::value << endl; //バイト境界は4
 
    func_test1(); // 計算通りなら,1000000*20bytes = 19.07MB
    func_test2(); // 計算通りなら,1000000*28bytes = 26.7MB
    
    return 0;
}

sleep

Re: std::set(vs2010)のメモリ使用量

#3

投稿記事 by sleep » 9年前

私はもっと確保されている認識です。
(※話がややこしいのでヘッダをいじっていない環境での話をさせてもらいます)

Windows10 64bit
Visual Studio 2015

以下のコードがあったとします。(x86)

コード:

struct A
{
	int a = 0;
	int b = 0;
	A(int i)
	{
		a = i;
		b = i * 2;
	}
	~A()
	{
		cout << "Aのデストラクタ" << endl;
	}
};

struct lessA : std::less<A>
{
	bool operator()(A a, A b) { return a.a > b.a; }
};

int main()
{
	{
		auto s = std::set<A, lessA>();
		s.emplace(1);
		s.emplace(2);
		s.emplace(3);
		s.emplace(4);
		s.emplace(5);
	}
}
そのコードによって呼び出さるallocatorの関数とその呼び出し順序は以下です。

コード:

auto s = std::set<A, lessA>();
-----------------------------------
allocator::allocate   : 0x0095BEA8 <- 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::allocate   : 0x0095F888 <- 8 byte  [struct std::_Container_proxy : 8 byte] * 1
allocator::construct  : struct std::_Container_proxy
-----------------------------------

s.emplace(1);
-----------------------------------
allocator::allocate   : 0x0095BEF0 <- 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : class A
-----------------------------------

s.emplace(2);
-----------------------------------
allocator::allocate   : 0x0095C130 <- 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : class A
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
-----------------------------------

s.emplace(3);
-----------------------------------
allocator::allocate   : 0x0095C1C0 <- 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : class A
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
-----------------------------------

s.emplace(4);
-----------------------------------
allocator::allocate   : 0x0095C298 <- 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : class A
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
-----------------------------------

s.emplace(5);
-----------------------------------
allocator::allocate   : 0x0095C208 <- 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : class A
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
Aのデストラクタ
-----------------------------------

ブロックから抜けるとき
-----------------------------------
allocator::destroy    : class A
Aのデストラクタ
allocator::deallocate : 0x0095BEF0 -> 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::destroy    : class A
Aのデストラクタ
allocator::deallocate : 0x0095C130 -> 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::destroy    : class A
allocator::Aのデストラクタ
allocator::deallocate : 0x0095C1C0 -> 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::destroy    : class A
Aのデストラクタ
allocator::deallocate : 0x0095C298 -> 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::destroy    : class A
Aのデストラクタ
allocator::deallocate : 0x0095C208 -> 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::destroy    : struct std::_Tree_node<class A,void *> *
allocator::destroy    : struct std::_Tree_node<class A,void *> *
allocator::destroy    : struct std::_Tree_node<class A,void *> *
allocator::deallocate : 0x0095BEA8 -> 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::destroy    : struct std::_Container_proxy
allocator::deallocate : 0x0095F888 -> 8 byte  [struct std::_Container_proxy : 8 byte] * 1
-----------------------------------

sleep

Re: std::set(vs2010)のメモリ使用量

#4

投稿記事 by sleep » 9年前

先ほど投稿した結果を確認できるプログラムです。
Linux環境でも実行できるはずです。
vs2010は・・・すいません。環境がないので動かなかったら手を入れてください(汗)
一応、私の環境での動作結果は貼り付けておきます。

class A のデストラクタで、表示後にメンバ変数の a と b を 0 へと変更してますが、その後に呼び出されていくデストラクタでは a と b は 0 へ変更されていないものが表示されていることが確認できるかと思います。
つまり、オブジェクトのコピーが各ノードに保持されているものと予想されます。

以上の理由から、私は やまさんが予想されていた量よりたくさんのメモリが確保および使用されているものと推測します。

コード:

//g++ -std=c++14 -o test test.cpp
#include <iostream>
#include <string>
#include <memory>
#include <new>
#include <cstddef>
#include <stdexcept>

template <class T>
class custom_allocator
{
public:
	static_assert(!std::is_const<T>::value, "custom_allocator<const T> is ill-formed.");

	using value_type = T;
	using pointer = value_type*;
	using const_pointer = const value_type*;
	using reference = value_type&;
	using const_reference = const value_type&;
	using size_type = std::size_t;
	using difference_type = std::ptrdiff_t;

	template <class Other>
	struct rebind { using other = custom_allocator<Other>; };
	pointer address(reference value) const { return &value; }
	const_pointer address(const_reference value) const { return &value; }
	constexpr size_type max_size() const noexcept { return static_cast<size_type>(-1) / sizeof(T); }

	template <class Other>
	custom_allocator<value_type>& operator = (const custom_allocator<Other>&) const { return *this; }
	inline bool operator == (const custom_allocator&) const { return true; }
	inline bool operator != (const custom_allocator&) const { return false; }

	custom_allocator() noexcept {}
	custom_allocator(const custom_allocator&) noexcept {}
	template <class Other>
	custom_allocator(const custom_allocator<Other>&) noexcept {}

	pointer allocate(size_type count, const void* = 0) const throw(std::bad_alloc, std::length_error)
	{
		if (count == 0) return nullptr;
		if (count > max_size()) throw std::length_error("custom_allocator<value_type>::allocate(size_type count, const void* = 0)  ->   count > max_size()");

		void* const p = ::operator new (sizeof(value_type) * count, std::nothrow);
		if (p == nullptr) throw std::bad_alloc();

		std::cout << "allocator::allocate   : " << std::hex << p << std::dec << " <- " << sizeof(value_type) * count << " byte  [" << typeid(value_type).name() << " : " << sizeof(value_type) << " byte] * " << count << std::endl;

		return static_cast<pointer>(p);
	}
	void deallocate(pointer p, size_type count) const
	{
		if (p == nullptr) return;
		::operator delete((void*)p);

		std::cout << "allocator::deallocate : " << std::hex << p << std::dec << " -> " << sizeof(value_type) * count << " byte  [" << typeid(value_type).name() << " : " << sizeof(value_type) << " byte] * " << count << std::endl;
	}

	template <class ObjType, class... Args>
	void construct(ObjType *p, Args&&... args) const
	{
		std::cout << "allocator::construct  : " << typeid(ObjType).name() << std::endl;

		::new((void*)p) ObjType(std::forward<Args>(args)...);
	}
	template <class ObjType>
	void destroy(ObjType *p) const
	{
		std::cout << "allocator::destroy    : " << typeid(ObjType).name() << std::endl;

		p->~ObjType();
	}
};

#include <iostream>
#include <string>
#include <set>
using namespace std;

class A
{
public:
	int a = 0;
	int b = 0;
	A(int i)
	{
		a = i;
		b = i * 2;
	}
	~A()
	{
		cout << "Aのデストラクタ       :(a=" << a << ", b=" << b << ")" << endl;
		a = 0;
		b = 0;
	}
};

class lessA : std::less<A>
{
public:
	bool operator()(A a, A b) { return a.a > b.a; }
};

int main()
{
#ifdef _WIN32
	_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
#endif

	{
		cout << R"(std::set<A, lessA, custom_allocator<A>> s;)" << endl;
		cout << "-----------------------------------" << endl;
		std::set<A, lessA, custom_allocator<A>> s;
		cout << "-----------------------------------" << endl << endl;

		cout << R"(s.emplace(1);)" << endl;
		cout << "-----------------------------------" << endl;
		s.emplace(1);
		cout << "-----------------------------------" << endl << endl;

		cout << R"(s.emplace(2);)" << endl;
		cout << "-----------------------------------" << endl;
		s.emplace(2);
		cout << "-----------------------------------" << endl << endl;

		cout << R"(s.emplace(3);)" << endl;
		cout << "-----------------------------------" << endl;
		s.emplace(3);
		cout << "-----------------------------------" << endl << endl;

		cout << R"(s.emplace(4);)" << endl;
		cout << "-----------------------------------" << endl;
		s.emplace(4);
		cout << "-----------------------------------" << endl << endl;

		cout << R"(s.emplace(5);)" << endl;
		cout << "-----------------------------------" << endl;
		s.emplace(5);
		cout << "-----------------------------------" << endl << endl;

		cin.ignore();

		cout << R"(ブロックから抜けるとき)" << endl;
		cout << "-----------------------------------" << endl;
	}
	cout << "-----------------------------------" << endl << endl;
	cin.ignore();
}
実行結果

コード:

std::set<A, lessA, custom_allocator<A>> s;
-----------------------------------
allocator::allocate   : 00F2CD80 <- 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::allocate   : 00F301B0 <- 8 byte  [struct std::_Container_proxy : 8 byte] * 1
allocator::construct  : struct std::_Container_proxy
-----------------------------------

s.emplace(1);
-----------------------------------
allocator::allocate   : 00F2D008 <- 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : class A
-----------------------------------

s.emplace(2);
-----------------------------------
allocator::allocate   : 00F2D290 <- 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : class A
Aのデストラクタ       :(a=2, b=4)
Aのデストラクタ       :(a=1, b=2)
Aのデストラクタ       :(a=1, b=2)
Aのデストラクタ       :(a=2, b=4)
-----------------------------------

s.emplace(3);
-----------------------------------
allocator::allocate   : 00F2D098 <- 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : class A
Aのデストラクタ       :(a=3, b=6)
Aのデストラクタ       :(a=1, b=2)
Aのデストラクタ       :(a=1, b=2)
Aのデストラクタ       :(a=3, b=6)
Aのデストラクタ       :(a=3, b=6)
Aのデストラクタ       :(a=2, b=4)
Aのデストラクタ       :(a=2, b=4)
Aのデストラクタ       :(a=3, b=6)
-----------------------------------

s.emplace(4);
-----------------------------------
allocator::allocate   : 00F2D128 <- 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : class A
Aのデストラクタ       :(a=4, b=8)
Aのデストラクタ       :(a=2, b=4)
Aのデストラクタ       :(a=2, b=4)
Aのデストラクタ       :(a=4, b=8)
Aのデストラクタ       :(a=4, b=8)
Aのデストラクタ       :(a=3, b=6)
Aのデストラクタ       :(a=3, b=6)
Aのデストラクタ       :(a=4, b=8)
-----------------------------------

s.emplace(5);
-----------------------------------
allocator::allocate   : 00F2D3F8 <- 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : struct std::_Tree_node<class A,void *> *
allocator::construct  : class A
Aのデストラクタ       :(a=5, b=10)
Aのデストラクタ       :(a=2, b=4)
Aのデストラクタ       :(a=2, b=4)
Aのデストラクタ       :(a=5, b=10)
Aのデストラクタ       :(a=5, b=10)
Aのデストラクタ       :(a=3, b=6)
Aのデストラクタ       :(a=3, b=6)
Aのデストラクタ       :(a=5, b=10)
Aのデストラクタ       :(a=5, b=10)
Aのデストラクタ       :(a=4, b=8)
Aのデストラクタ       :(a=4, b=8)
Aのデストラクタ       :(a=5, b=10)
-----------------------------------


ブロックから抜けるとき
-----------------------------------
allocator::destroy    : class A
Aのデストラクタ       :(a=1, b=2)
allocator::deallocate : 00F2D008 -> 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::destroy    : class A
Aのデストラクタ       :(a=2, b=4)
allocator::deallocate : 00F2D290 -> 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::destroy    : class A
Aのデストラクタ       :(a=3, b=6)
allocator::deallocate : 00F2D098 -> 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::destroy    : class A
Aのデストラクタ       :(a=4, b=8)
allocator::deallocate : 00F2D128 -> 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::destroy    : class A
Aのデストラクタ       :(a=5, b=10)
allocator::deallocate : 00F2D3F8 -> 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::destroy    : struct std::_Tree_node<class A,void *> *
allocator::destroy    : struct std::_Tree_node<class A,void *> *
allocator::destroy    : struct std::_Tree_node<class A,void *> *
allocator::deallocate : 00F2CD80 -> 24 byte  [struct std::_Tree_node<class A,void *> : 24 byte] * 1
allocator::destroy    : struct std::_Container_proxy
allocator::deallocate : 00F301B0 -> 8 byte  [struct std::_Container_proxy : 8 byte] * 1
-----------------------------------

sleep

Re: std::set(vs2010)のメモリ使用量

#5

投稿記事 by sleep » 9年前

・・・違いますね。
コピーが生成されているのは、lessのオーバーライドの引数ですね。
sleep さんが書きました:

コード:

class lessA : std::less<A>
{
public:
	bool operator()(A a, A b) { return a.a > b.a; }
};
以下だと呼ばれないですね。(const & へ変更)

コード:

class lessA : std::less<A>
{
public:
	bool operator()(const A& a, const A& b) { return a.a > b.a; }
};
なので、lessのオーバーライドを const & で受け取ってみた場合、プライベートワーキングセットが減るかもしれません。
やま さんが書きました:

コード:

struct Test1LessThan : less<struct test1> {
    bool operator()( struct test1 a, struct test1 b ) { return a.b > b.b; }
};
 
struct Test2LessThan : less<struct test2>{
    bool operator()( struct test2 a, struct test2 b ) { return a.b > b.b; }
};

sleep

Re: std::set(vs2010)のメモリ使用量

#6

投稿記事 by sleep » 9年前

結局減らせたとしても、スレッドスタックで偶々PAGE_GUARD属性のページを踏んだ場合にコミットされて増えるのは高々4096バイトであり、モヤモヤしていたため、以下のコードをWinDbg上で実行してヒープの状況を確認してみました。

コード:

#include <iostream>
#include <string>
#include <set>
using namespace std;

class A
{
public:
	int a = 0;
	int b = 0;
	A(int i)
	{
		a = i;
		b = i*2;
	}
};

class lessA : std::less<A>
{
public:
	bool operator()(const A& a, const A& b) { return a.a > b.a; }
};

int main()
{
	set<A, lessA> s;
	for (int i = 0; i < 1000000; i++)
	{
		s.emplace(i);
	}
	__asm int 3;
}
で、調べていて気付いたんですが、class Aは上記でも以下でも(つまり、20byteでも24byteでも)

コード:

class A
{
public:
	int a = 0;
	A(int i)
	{
		a = i;
	}
};
Windows10上では計上されているプライベートワーキングセットに違いはありませんでした。
どちらも実行毎に多少のブレはありますが、どちらの方が決まって多いとかは無いですね。

タスクマネージャで確認したプライベートワーキングセットのサイズ(20byte、24byte共に以下の辺りを上下)

コード:

1000000件実行時
32044k(exeを通常実行)
47404k(wingdbから起動後、gコマンド使用後)
しかも、セグメントのコミットされていたサイズがどちらも固定で一緒ですね。

20byteの場合(wingdbから起動後、gコマンド使用後)

コード:

0:000> !heap -h
Index   Address  Name      Debugging options enabled
  1:   00530000 
    Segment at 00530000 to 0062f000 (000ff000 bytes committed)
    Segment at 00aa0000 to 00b9f000 (000ff000 bytes committed)
    Segment at 00c40000 to 00e3f000 (001ff000 bytes committed)
    Segment at 00e40000 to 0123f000 (003ff000 bytes committed)
    Segment at 01240000 to 01a3f000 (007ff000 bytes committed)
    Segment at 01a40000 to 02a0f000 (00fcf000 bytes committed)
    Segment at 02a10000 to 039df000 (00e08000 bytes committed)
  2:   00c30000 
    Segment at 00c30000 to 00c3f000 (00003000 bytes committed)
24byteの場合(wingdbから起動後、gコマンド使用後)

コード:

0:000> !heap -h
Index   Address  Name      Debugging options enabled
  1:   006d0000 
    Segment at 006d0000 to 007cf000 (000ff000 bytes committed)
    Segment at 00a20000 to 00b1f000 (000ff000 bytes committed)
    Segment at 00b20000 to 00d1f000 (001ff000 bytes committed)
    Segment at 00e10000 to 0120f000 (003ff000 bytes committed)
    Segment at 01210000 to 01a0f000 (007ff000 bytes committed)
    Segment at 01a10000 to 029df000 (00fcf000 bytes committed)
    Segment at 029e0000 to 039af000 (00e08000 bytes committed)
  2:   00a10000 
    Segment at 00a10000 to 00a1f000 (00003000 bytes committed)
1:の各セグメントの内訳(10進数)

コード:

 1044480(Segmentサイズ:1048576[65536 * 16] = ヘッダブロック:4096 + コミット済みサイズ:1044480[4096 * 255])
 1044480(Segmentサイズ:1048576[65536 * 16] = ヘッダブロック:4096 + コミット済みサイズ:1044480[4096 * 255])
 2093056(Segmentサイズ:2097152[65536 * 32] = ヘッダブロック:4096 + コミット済みサイズ:2093056[4096 * 511])
 4190208(Segmentサイズ:4194304[65536 * 64] = ヘッダブロック:4096 + コミット済みサイズ:4190208[4096 * 1023])
 8384512(Segmentサイズ:8388608[65536 * 128] = ヘッダブロック:4096 + コミット済みサイズ:8384512[4096 * 2047])
16576512(Segmentサイズ:16580608[65536 * 253] = ヘッダブロック:4096 + コミット済みサイズ:16576512[4096 * 4047])
14712832(Segmentサイズ:16580608[65536 * 253] = ヘッダブロック:4096 + コミット済みサイズ:14712832[4096 * 3592] + 未コミットサイズ:1863680[4096 * 455])
リージョンのコミットサイズ合計(スレッドスタック含む)がプライベートワーキングセットとして計上されている様なので、20byteと24byteでコミットサイズが同じということは単純に未使用領域が多大にあると考えてしまっていいんじゃないでしょうか?
もしそれが事実であれば、単純にsetではallocatorで確保されたサイズのみが使用されていると言えます。

1044480(Segmentサイズ:1048576[65536 * 16] = ヘッダブロック:4096 + コミット済みサイズ:1044480[4096 * 255])
1044480(Segmentサイズ:1048576[65536 * 16] = ヘッダブロック:4096 + コミット済みサイズ:1044480[4096 * 255])
2093056(Segmentサイズ:2097152[65536 * 32] = ヘッダブロック:4096 + コミット済みサイズ:2093056[4096 * 511])
4190208(Segmentサイズ:4194304[65536 * 64] = ヘッダブロック:4096 + コミット済みサイズ:4190208[4096 * 1023])
8384512(Segmentサイズ:8388608[65536 * 128] = ヘッダブロック:4096 + コミット済みサイズ:8384512[4096 * 2047])

ここまでだと合計しても 16756736 byte (16364 k)なので、20byte 1000000件を保存する容量に満たず、

16576512(Segmentサイズ:16580608[65536 * 253] = ヘッダブロック:4096 + コミット済みサイズ:16576512[4096 * 4047])

を加えた状態であれば 33333248 (32552 k) となるので、24byte 1000000件も十分保存できる容量です。
上記は WinDbg上で gコマンド実行後の状態でのコミットサイズなので、傾向でコミットサイズが決まっているとすれば、exe を直接実行した場合でも 20byteと24byteでプライベートワーキングセットの値に違いが出ないのも頷けます。

試しで 700000 で 20byteと24byteでそれぞれやってみましたが、予想通りコミットサイズは同じになっていました。(プライベートワーキングセットも同じでした。)

20byteの場合(wingdbから起動後、gコマンド使用後)

コード:

0:000> !heap -h
Index   Address  Name      Debugging options enabled
  1:   00930000 
    Segment at 00930000 to 00a2f000 (000ff000 bytes committed)
    Segment at 00a30000 to 00b2f000 (000ff000 bytes committed)
    Segment at 00c00000 to 00dff000 (001ff000 bytes committed)
    Segment at 00e00000 to 011ff000 (003ff000 bytes committed)
    Segment at 01200000 to 019ff000 (007ff000 bytes committed)
    Segment at 01a00000 to 029cf000 (00fcf000 bytes committed)
    Segment at 029d0000 to 0399f000 (0004c000 bytes committed)
  2:   00bf0000 
    Segment at 00bf0000 to 00bff000 (00003000 bytes committed)
24byteの場合(wingdbから起動後、gコマンド使用後)

コード:

0:000> !heap -h
Index   Address  Name      Debugging options enabled
  1:   008b0000 
    Segment at 008b0000 to 009af000 (000ff000 bytes committed)
    Segment at 009b0000 to 00aaf000 (000ff000 bytes committed)
    Segment at 00b30000 to 00d2f000 (001ff000 bytes committed)
    Segment at 00d30000 to 0112f000 (003ff000 bytes committed)
    Segment at 01130000 to 0192f000 (007ff000 bytes committed)
    Segment at 01930000 to 028ff000 (00fcf000 bytes committed)
    Segment at 02900000 to 038cf000 (0004c000 bytes committed)
  2:   00b20000 
    Segment at 00b20000 to 00b2f000 (00003000 bytes committed)
なので、Windows7の場合は20byte*1000000の場合と24byte*1000000の場合で傾向によるコミットされるサイズ(境界線)に違いがあっただけかもしれません。
私がWinDbgを使用してWindows10上で調べた感じだと、予約とコミットサイズは何かの基準で行われている可能性が高く、setが直接使用しているメモリ容量とプライベートワーキングセットの容量は開きが大きい可能性があります。

閉鎖

“C言語何でも質問掲示板” へ戻る