ホーム 主筆 その他ソフト その他情報 Syuhitu.org English

Windows関連

スクリーンセーバー作成法

半透明ウインドウの性能

bootfont.bin

キャビネット形式

ウインドウスタイルをいじる

Java製ソフトをServiceに登録する

イベントログにメッセージを出力する

コントロールパネルにアイコンを追加する

スクリプトによる拡張1

スクリプトによる拡張2

ガジェットの作成

大容量メモリ

メモリ搭載量の下限に挑む

スパースファイルにする

表示されるアイコンの種類を調べてみた

メモリマップIOとエラー処理

ファイルを作る順番と速度の関係

Cryptography API: Next Generationを使う

Windows 10のアクセントカラー

iSCSIディスクにバックアップを取る

サーバプロセスを分離して実装する

サーバプロセスを分離して実装する - F#

レジストリに大量に書き込む

Solaris関連

OpenGL

Solaris設定

ディレクトリの読み込み

主筆プラグイン開発

マルチスレッドでの開発

door

音を出す

Blade100の正しい虐め方

パッケージの作成

画像入出力

BMPファイル

ICOファイル

ANIファイル

JPEGファイル

減色アルゴリズム

減色アルゴリズムの並列化

その他アルゴリズムなど

自由軸回転

Base64

文字列操作

CPU利用率の取得

正規表現ライブラリ

メタボールを作る

メタボールを作る2

正規表現とNFA・DFA

C言語の構文解析

液晶ディスプレイを解体してみた

iSCSIの理論と実装

単一フォルダにファイルを沢山作る

USB-HUBのカスケード接続

SafeIntの性能

VHDファイルのフォーマット

USBメモリに書き込み続けてみた

単一フォルダにファイルを沢山作る

2015年1月7日公開

1つのフォルダに中に作るファイルの個数はあまり多くない方がよいとされている。

具体的にいくつかと言われると諸説あるようだが、数千とか数万がいいところだという。

じゃ、実際に一杯作ったらどうなるのか。やってみた。

環境

実験で使用したのは以下の環境である。

  • OS : Windows 7 Professional 64bit
  • CPU : Core i7 920
  • メモリ : 24GB
  • ディスク : INTEL M25 SSD 180GB × 4(RAID1E) 実効容量 360GB
  • RAIDコントローラ : Adaptec RAID 2405
  • ファイルシステム : NTFS

今となっては大分古くなってきた感じもするが、まぁしかたがない。

処理内容

冒頭にも記載したとおり、単一のフォルダの中にファイルを沢山作る。

その際、ファイル名を通番で作成すると、ディレクトリエントリの管理で何らかの最適化が働くのではないか、実際の使用状況とは大きく異なるのではないかという批判もありそうなので、ファイル名はハッシュ値をとりできるだけランダムになる様、配慮してみる。

また、一方的に追加のみを行うと、これまた実際の使用状況と……という話に陥りそうなので、途中でファイルの削除も入れることにした。すなわち、ある程度ファイルを作ったら、その内半分を削除するという処理をひたすら繰り返すことにする。

100万個作る

どれぐらい時間がかかるのかも、何が起きるのかも分からないので、おっかなびっくりでまずは100万個作成してみることにする。

まぁ、これでも世間一般でやめた方がいいと言われるレベルを超えているはずだが。

実行したプログラム

大して参考にもならないだろうが、以下のようなプログラムを実行している。

#include "stdafx.h"
#include <stdio.h>
#include <Windows.h>
#include <Bcrypt.h>
#include <omp.h>

void Bin2Hex( char *p1, char *p2, int len )
{
  for ( int i = 0; i < len; i++ ) {
    (*p2) = "0123456789ABCDEF"[ ( (*p1) >> 4 ) & 0x0F ];
    p2++;
    (*p2) = "0123456789ABCDEF"[ (*p1) & 0x0F ];
    p2++;
    p1++;
  }
  (*p2) = 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
  BCRYPT_ALG_HANDLE hAlgorithm;
  BCRYPT_HASH_HANDLE hHash;
  DWORD ObjectSize = 0;
  DWORD Result = 0;
  PUCHAR pHashObject = NULL;
  int i = 0;
  DWORD j = 0;
  DWORD Tick = 0;
  DWORD Tick2 = 0;
  char buf1[128];
  char buf2[128];
  char buf3[128] = "D:\\MO\\VC++\\mkfiles\\test\\";
  char *buf3_p = buf3 + strlen( buf3 );
  HANDLE h = 0;

  // アルゴリズムプロバイダを取得する
  BCryptOpenAlgorithmProvider( &hAlgorithm, BCRYPT_SHA1_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0 );
  
  // ハッシュ値算出用のオブジェクトのサイズを取得する
  BCryptGetProperty(
    hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)( &ObjectSize ), sizeof( ObjectSize ), &Result, 0
  );

  pHashObject = (UCHAR*)malloc( ObjectSize );

  for ( j = 0; j < 1000; j++ ) {

    // 開始時間
    Tick = ::GetTickCount();

    // ファイルを2000個作る
    for ( i = 0; i < 2000; i++ ) {

      // ファイル名を決定する
      sprintf_s( buf1, "%016d", j * 2000 + i );

      // ハッシュ値を取得する
      BCryptCreateHash( hAlgorithm, &hHash, pHashObject, ObjectSize, NULL, 0, 0 );
      BCryptHashData( hHash, (PUCHAR)buf1, 16, 0 );
      BCryptFinishHash( hHash, (PUCHAR)buf2, 20, 0 );
      BCryptDestroyHash( hHash );

      // バイナリの値を16進数の表現に変換する
      Bin2Hex( buf2, buf3_p, 20 );

      // ファイルを作成する
      h = CreateFileA( buf3, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
      if ( h == INVALID_HANDLE_VALUE )
        printf( "エラー, ファイル %d の作成に失敗\n", j * 2000 + i );
      else
        CloseHandle( h );

    }

    // 所要時間を表示
    Tick2 = GetTickCount() - Tick;
    printf( "作成, ファイル %d 〜 %d, %d\n", j * 2000, j * 2000 + 1999, Tick2 );
    fprintf( stderr, "作成, ファイル %d 〜 %d, %d\n", j * 2000, j * 2000 + 1999, Tick2 );

    // 開始時間
    Tick = ::GetTickCount();

    // ファイルを1000個削除する
    for ( i = 0; i < 1000; i++ ) {
      // ファイル名を決定する
      sprintf_s( buf1, "%016d", j * 2000 + i );

      // ハッシュ値を取得する
      BCryptCreateHash( hAlgorithm, &hHash, pHashObject, ObjectSize, NULL, 0, 0 );
      BCryptHashData( hHash, (PUCHAR)buf1, 16, 0 );
      BCryptFinishHash( hHash, (PUCHAR)buf2, 20, 0 );
      BCryptDestroyHash( hHash );

      // バイナリの値を16進数の表現に変換する
      Bin2Hex( buf2, buf3_p, 20 );

      // ファイルを削除する
      if ( !DeleteFileA( buf3 ) )
        printf( "エラー, ファイル %d の削除に失敗\n", j * 2000 + i );
    }

    // 所要時間を表示
    Tick2 = GetTickCount() - Tick;
    printf( "削除, ファイル %d 〜 %d, %d\n", j * 2000, j * 2000 + 999, Tick2 );
    fprintf( stderr, "削除, ファイル %d 〜 %d, %d\n", j * 2000, j * 2000 + 999, Tick2 );
  }

  return 0;
}

ハッシュ値を取得する処理が入っていて分かりにくくなっているが、やっていることはファイルを2,000個作成して、1,000個削除するという処理を1,000回繰り返しているだけである。

その途中、ファイルを2,000個作成する処理時間と、1,000個削除する処理時間を測定して出力している。

実行結果

こうなった。

1つのフォルダに下に100万個作ってみた。

ファイルの作成に要した処理時間

縦軸は時間(ミリ秒)、横軸はファイルを2,000個作成する処理を1回としたときの回次である。すなわち、一番左端は0個目の作成から1,999個目の作成に要した時間を示しており、2番目は2,000個目から3,999個目の作成に要した時間を示しており、一番右端は1,998,000個目から1,998,999個目の作成に要した時間を示している。

ファイルの削除に要した処理時間

作成と同様に、縦軸は時間(ミリ秒)である。横軸はファイルを1000個ずつ削除した時の回次である。作成の時と同様である。ただし、削除したファイルの個数は1,000個ずつである。

(※ 上でも書いたが、ファイルの作成は200万毎個分行い、その内100万個分を削除している)

上記のグラフは棒グラフではない。紛らわしいが、折れ線グラフである。

こうしてみると、ファイル2,000個の作成には、少なくとも400ms弱要し、何らかの理由で突発的に処理時間が長くなる時がある。ファイル1,000個の削除も同様に、少なくとも200ms弱要し、突発的に処理時間が長くなる時がある様だ。

本来であれば、徐々に処理時間が長くなるようなグラフになる事を期待していたのだが、正直言ってこれではなんだか分からない、というのが正直な感想だ。

ということで、もうちょっと頑張ってみることにしてみた。

1,000万個作る

2桁増やして、20万個作成して10万個削除する処理を1,000回繰り返すことで1億個作成してみるかと思ったのだが、途中で時間がかかりすぎていやになったから、1,000万個作成した時点で止めてしまった。

プログラムは、100万個作成した時とほぼ同じだから割愛する。

一応、作ってみた証拠の画像を示す。

100万個作った時と同じようにエクスプローラーで表示しようとしたが、時間がかかりすぎて途中で止めてしまった。ここまで来ると、エクスプローラーでの操作はあきらめた方がいいようだ。なお、時間はかかっていたが、異常終了するようなことにはならなかった。少なくとも、俺がウインドウを閉じるまでは。

処理時間のグラフを示す。

ファイルの作成に要した処理時間

グラフの書き方は先ほどと同様、縦軸は時間(ミリ秒)であり、横軸はファイルを20万個作成する処理を1回としたときの回次である。

ファイルの削除に要した処理時間

縦軸は時間(ミリ秒)であり、横軸はファイルを10万個作成する処理を1回としたときの回次である。

2,000個の作成を単位とした場合よりも、傾向が解りやすくなっている気がする。

何となく、ファイル数が多くなるに従って、処理が遅くなってきているのではないだろうか? それも、恐らくは徐々に悪化の度合いが低下していっていることから、直感的にO(log n)に比例するグラフを描いているように見える。

1億個作る

気を取り直して、もう一度1億個分の作成に挑戦する。

なお、時間がかかってたまらないから、今度は単純に10万個作成する処理を1,000回繰り返す事にする。つまり、削除するのをやめる。

プログラムは大したものではないので、示したところで仕方がないが、一応参考までに載せておく。

#include "stdafx.h"
#include <stdio.h>
#include <Windows.h>
#include <Bcrypt.h>
#include <omp.h>

void Bin2Hex( char *p1, char *p2, int len )
{
  for ( int i = 0; i < len; i++ ) {
    (*p2) = "0123456789ABCDEF"[ ( (*p1) >> 4 ) & 0x0F ];
    p2++;
    (*p2) = "0123456789ABCDEF"[ (*p1) & 0x0F ];
    p2++;
    p1++;
  }
  (*p2) = 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
  BCRYPT_ALG_HANDLE hAlgorithm;
  BCRYPT_HASH_HANDLE hHash;
  DWORD ObjectSize = 0;
  DWORD Result = 0;
  PUCHAR pHashObject = NULL;
  int i = 0;
  DWORD j = 0;
  DWORD Tick = 0;
  DWORD Tick2 = 0;
  char buf1[128];
  char buf2[128];
  char buf3[128] = "D:\\MO\\VC++\\mkfiles\\test\\";
  char *buf3_p = buf3 + strlen( buf3 );
  HANDLE h = 0;

  // アルゴリズムプロバイダを取得する
  BCryptOpenAlgorithmProvider( &hAlgorithm, BCRYPT_SHA1_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0 );
  
  // ハッシュ値算出用のオブジェクトのサイズを取得する
  BCryptGetProperty(
    hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)( &ObjectSize ), sizeof( ObjectSize ), &Result, 0
  );

  pHashObject = (UCHAR*)malloc( ObjectSize );

  for ( j = 0; j < 1000; j++ ) {

    // 開始時間
    Tick = ::GetTickCount();

    // ファイルを200000個作る
    for ( i = 0; i < 100000; i++ ) {

      // ファイル名を決定する
      sprintf_s( buf1, "%016d", j * 200000 + i );

      // ハッシュ値を取得する
      BCryptCreateHash( hAlgorithm, &hHash, pHashObject, ObjectSize, NULL, 0, 0 );
      BCryptHashData( hHash, (PUCHAR)buf1, 16, 0 );
      BCryptFinishHash( hHash, (PUCHAR)buf2, 20, 0 );
      BCryptDestroyHash( hHash );

      // バイナリの値を16進数の表現に変換する
      Bin2Hex( buf2, buf3_p, 20 );

      // ファイルを作成する
      h = CreateFileA( buf3, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
      if ( h == INVALID_HANDLE_VALUE )
        printf( "エラー, ファイル %d の作成に失敗\n", j * 200000 + i );
      else
        CloseHandle( h );

    }

    // 所要時間を表示
    Tick2 = GetTickCount() - Tick;
    printf( "作成, ファイル %d 〜 %d, %d\n", j * 200000, j * 200000 + 199999, Tick2 );
    fprintf( stderr, "作成, ファイル %d 〜 %d, %d\n", j * 200000, j * 200000 + 199999, Tick2 );
  }

  return 0;
}

一応、作った。

処理時間はこうなった。

何事もやってみるもんだ。また違う結果が出てきた。

左端に見えている、突発的に処理時間が長くなっている部分については特異値として無視したとしても、だとしても傾向がよく分からない。

途中まではあまり処理時間が長くなる事はないようだが、途中、4千万個を超えた当たりから明らかにグラフが折れ曲がり、線形に比例して処理時間が長くなっている。

NTFSのアルゴリズムが公表されていない以上結局は何も解らないのだが、しかしまぁ、何かファイル数に応じてアルゴリズムを変えるような操作でも行っているだろうか?

結論

いくらやってみたところで、絶対値は結局のところマシンスペックに依存するので確たる事はいえない。

しかしながら、世の中で言われているように、数千個を超えたらだめとか、そんなことはないようだ。100万個ぐらいであればはどうと言ったことはなかった。

エクスプローラーで操作することを諦めれば、4千万個までは処理時間が大幅に遅延することもないから、あえてフォルダを分割するとか、そんな工夫も意味がない様に思われる。

 


連絡先 - サイトマップ - 更新履歴
Copyright (C)  2000 - 2016 nabiki_t All Rights Reserved.