31

私は 26901^2*sizeof(double) である C の malloc を持っています

これにより、ここで最大の値が何であるかを考えさせられました。

また、この 2D 配列にアクセスするためのマクロを定義する際に問題はありますか?

 #define DN(i,j) ((int)i * ny + (int)j)

これは私にとってはうまくいかないようです-または、少なくともそうであるとは確信していません。A[DN(indx,jndx)] が実際に何を見ているかを教えてくれるマクロで totalview ダイブを作成する方法がわかりません。

4

6 に答える 6

45

観察

glibc が使用するような典型的なアロケーターを想定すると、いくつかの観察事項があります。

  1. メモリが実際に使用されるかどうかにかかわらず、領域は仮想メモリ内で連続して予約する必要があります。
  2. 最大の空き連続領域は、既存のメモリ領域のメモリ使用量と、これらの領域の可用性に依存しますmalloc
  3. マッピングの方法は、アーキテクチャと OS によって異なります。さらに、メモリ領域を取得するための基になるシステム コールは、これらのプラクティスの影響を受けます (ページを取得するために をmalloc呼び出すなどmmap)。

実験

可能な限り最大のブロックを割り当てる簡単なプログラムを次に示します (次のようにコンパイルしgcc largest_malloc_size.c -Wall -O2ます:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static void *malloc_wrap(size_t size)
{
    void *p = malloc(size);
    if (p) {
        printf("Allocated %zu bytes from %p to %p\n", size, p, p + size);
    }
    else {
        printf("Failed to allocated %zu bytes\n", size);
    }
    return p;
}

int main()
{
    size_t step = 0x1000000;
    size_t size = step;
    size_t best = 0;
    while (step > 0)
    {
        void *p = malloc_wrap(size);
        if (p) {
            free(p);
            best = size;
        }
        else {
            step /= 0x10;
        }
        size += step;
    }
    void *p = malloc_wrap(best);
    if (p) {
        pause();
        return 0;
    }
    else {
        return 1;
    }
}

私のマシンで上記のプログラム ( ./a.out) を実行すると、次の結果が得られます。Linux stanley 2.6.32-24-generic-pae #39-Ubuntu SMP Wed Jul 28 07:39:26 UTC 2010 i686 GNU/Linux

<snip>
Allocated 2919235584 bytes from 0x9763008 to 0xb7763008
Allocated 2936012800 bytes from 0x8763008 to 0xb7763008
Failed to allocated 2952790016 bytes
Failed to allocated 2953838592 bytes
Failed to allocated 2953904128 bytes
Failed to allocated 2953908224 bytes
Allocated 2936012800 bytes from 0x85ff008 to 0xb75ff008

これは、正確に 2800MiB の割り当てです。からの関連するマッピングの観察/proc/[number]/maps:

<snip>
0804a000-0804b000 rw-p 00001000 08:07 3413394    /home/matt/anacrolix/public/stackoverflow/a.out
085ff000-b7600000 rw-p 00000000 00:00 0          [heap]
b7600000-b7621000 rw-p 00000000 00:00 0 
b7621000-b7700000 ---p 00000000 00:00 0 
b7764000-b7765000 rw-p 00000000 00:00 0 
b7765000-b78b8000 r-xp 00000000 08:08 916041     /lib/tls/i686/cmov/libc-2.11.1.so
<snip>
bfc07000-bfc1c000 rw-p 00000000 00:00 0          [stack]

結論

プログラム データとコードの間の領域でヒープが拡張されているように見えます。共有ライブラリ マッピングは、ユーザー/カーネル メモリ空間の境界(このシステムでは明らかに 3G/1G)に対してぴったりと位置しています。

この結果は、malloc を使用して割り当て可能な最大スペースが次のようになることを示しています。

  1. ユーザー空間領域 (例では 3GB)
  2. ヒープの先頭へのオフセットを差し引いたもの (プログラム コードとデータ)
  3. メイン スレッド スタック用に予約されたスペースを減らす
  4. マップされたすべての共有ライブラリが占有するスペースが少ない
  5. 最後に、ヒープで使用可能な領域内で基礎となるシステム コールによって検出できる最大の連続領域 (他のマッピングによって断片化される可能性があります)。

ノート

glibc と Linux の実装に関しては、次のマニュアル スニペットが非常に重要です。

malloc

   Normally, malloc() allocates memory from the heap, and adjusts the size
   of the heap as required, using sbrk(2).  When allocating blocks of mem‐
   ory larger than MMAP_THRESHOLD bytes, the glibc malloc() implementation
   allocates the memory as a  private  anonymous  mapping  using  mmap(2).
   MMAP_THRESHOLD  is  128  kB  by  default,  but is adjustable using mal‐
   lopt(3).

mmap

   MAP_ANONYMOUS
          The mapping is not backed by any file; its contents are initial‐
          ized to zero.

あとがき

このテストは x86 カーネルで行われました。非常に大きなメモリ領域が返されますが、x86_64 カーネルでも同様の結果が得られると思います。他のオペレーティング システムでは、マッピングの配置や大きなmallocの処理が異なる場合があるため、結果がかなり異なる可能性があります。

于 2010-08-15T04:13:42.547 に答える
11

それはあなたのmallocの実装に依存します!

ウィキペディアによると、「v2.3リリース以降、GNU Cライブラリ(glibc)は、それ自体がdlmallocv2.7.0に基づく変更されたptmalloc2を使用しています。」dlmallocは、DougLeaのmalloc実装を指します。この実装で注意すべき重要な点は、大規模なmallocは、オペレーティングシステムのメモリマップトファイル機能によって実現されるため、これらのブロックは、隣接するブロックを見つけるという多くの問題なしに、実際に非常に大きくなる可能性があることです。

于 2010-08-11T22:08:49.440 に答える
7

mallocの質問に回答します(指定していないOSによって異なります)。そのため、次のように定義します。

#define DN(i,j) ((int)i * ny + (int)j)

DN(a+b,c)誰かがそうするかもしれないので、完全に安全ではありません。

((int)a+b * ny + (int)c)

これはおそらくあなたが望んでいたものではありません。だからそこにたくさんの括弧を入れてください:

#define DN(i,j) ((int)(i) * ny + (int)(j))

DN(indx,jndx)を指しているのかを確認するにはprintf("%d\n",DN(indx,jndx));

于 2010-08-11T22:14:55.377 に答える
1

mallocの呼び出しのsizeパラメーターはsize_t型であり、実装によって異なります。詳細については、この質問を参照してください。

于 2010-08-11T22:13:11.810 に答える
1

これにより、ここで最大の値が何であるかを考えさせられました。

26'901^2 = 723'663'801. double が 8 バイトの場合、8GB 未満です。その量のメモリを割り当てることにまったく問題はなく、私のアプリは (64 ビット システムでは) より多くのメモリを定期的に割り当てます。(私が今まで見た中で最大のメモリ消費量は 420GB (640GB RAM の Solaris 10 numa システム上) で、最大連続ブロックは ~24GB でした。)

プラットフォームに依存するため、最大値を特定するのは困難です。32 ビット システムと同様に、ユーザー空間とカーネル空間の分割に依存します。現時点では、libc が割り当てることができる限界に達する前に、まず実際の物理 RAM の限界に達すると思います。(そして、カーネルは気にしません。固定するのに十分な RAM があるかどうかさえ考慮せずに、しばしば仮想メモリを拡張するだけです。)

于 2010-08-11T22:21:54.643 に答える
1

求める ことができる最大のメモリ ブロックmalloc()は、最大size_t値です。これSIZE_MAX<limits.h>. 正常に要求できる最大量は、オペレーティング システムと個々のマシンの構成によって明らかに異なります。

あなたのマクロは安全ではありません。32767 までの範囲のみが必要な変数を使用してインデックス計算を実行しintます。これよりも大きい値を指定すると、符号付きオーバーフローが発生し、未定義の動作が発生する可能性があります。size_tその型は有効な配列インデックスを保持できる必要があるため、おそらく として計算を行うのが最善です。

#define DN(i, j) ((size_t)(i) * ny + (size_t)(j))

i(ただし、またはに負の値をj指定すると、範囲をはるかに超えるインデックスが得られることに注意してください)。

于 2010-08-11T23:52:53.740 に答える