優先度を試す

nice(笑)ってきてたけど、正直、優先度について全く知らなかったっていう。
どれだけ効果があるのかな。


作ったプログラムとスクリプトの紹介

loop ... 40億回インクリメントを行うループ
loop2 ... 4万回インクリメントを行うループ(ループ毎にusleep(1000)するので、40秒かかる計算)
kick.sh ... 指定したコマンドを3回同時にバックグラウンド実行する。それぞれ、通常、nice, chrtを使う。

コードは最後。

環境

SのVPS(512MB)

[root@www43071u tmp]# uname -a
Linux www43071u.sakura.ne.jp 2.6.32-220.4.1.el6.x86_64 #1 SMP Tue Jan 24 02:13:44 GMT 2012 x86_64 x86_64 x86_64 GNU/Linux
[root@www43071u tmp]# cat /etc/redhat-release
CentOS release 6.2 (Final)
[root@www43071u tmp]# gcc -v
Using built-in specs.
Target: x86_64-redhat-linux
コンフィグオプション: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
スレッドモデル: posix
gcc version 4.4.6 20110731 (Red Hat 4.4.6-3) (GCC)

作業PC@VM

[root@localhost tmp]# uname -a
Linux localhost.localdomain 2.6.32-220.el6.x86_64 #1 SMP Tue Dec 6 19:48:22 GMT 2011 x86_64 x86_64 x86_64 GNU/Linux
[root@localhost tmp]# cat /etc/redhat-release
CentOS release 6.2 (Final)
[root@localhost tmp]# gcc -v
Using built-in specs.
Target: x86_64-redhat-linux
コンフィグオプション: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
スレッドモデル: posix
gcc version 4.4.6 20110731 (Red Hat 4.4.6-3) (GCC)

自宅鯖(nimono鯖)

[root@rying tmp]# uname -a
Linux rying.net 2.6.38.3 #4 SMP Sat Apr 30 20:21:58 JST 2011 i686 i686 i386 GNU/Linux
[root@rying tmp]# cat /etc/redhat-release
CentOS release 5.6 (Final)
[root@rying tmp]# gcc -v
Using built-in specs.
Target: i386-redhat-linux6E
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --disable-gnu-unique-object --enable-languages=c,c++,fortran --disable-libgcj --with-mpfr=/builddir/build/BUILD/gcc-4.4.4-20100726/obj-i386-redhat-linux6E/mpfr-install/ --with-ppl=/builddir/build/BUILD/gcc-4.4.4-20100726/obj-i386-redhat-linux6E/ppl-install --with-cloog=/builddir/build/BUILD/gcc-4.4.4-20100726/obj-i386-redhat-linux6E/cloog-install --with-tune=generic --with-arch=i586 --build=i386-redhat-linux6E
Thread model: posix
gcc version 4.4.4 20100726 (Red Hat 4.4.4-13) (GCC)


他のディストリビューションが怖いCentOS厨。
自宅鯖はおそらく、ふつーのカーネル

計測

コンパイル時にオプションはつけないで実施した。
最適化しないよう明示する-O0とかしたほうがよかったかも。

gcc -o loop loop.c
gcc -o loop2 loop2.c

SのVPS(512MB)

# ./kick.sh ./loop
Start normal[pid:10676]
Start chrt[pid:10677]
Start nice[pid:10678]
Finish chrt[pid:10677] (12890000clock, time:12.920274)
Finish nice[pid:10678] (13500000clock, time:13.719407)
Finish normal[pid:10676] (14040000clock, time:22.026900)

プロンプトが全く帰ってこなかった。chrt終了後は普通に操作できた。

# ./kick.sh ./loop2
Start normal[pid:10722]
Start chrt[pid:10723]
Start nice[pid:10724]
Finish chrt[pid:10723] (960000clock, time:47.292046)
Finish nice[pid:10724] (960000clock, time:48.779575)
Finish normal[pid:10722] (980000clock, time:49.117561)

sleepの精度が悪いと思えばおおむね期待通り。

作業PC@VM

# ./kick.sh ./loop & top
Start normal[pid:12341]
Start chrt[pid:12342]
Start nice[pid:12343]
Finish chrt[pid:12342] (10150000clock, time:10.656801)
Finish nice[pid:12343] (10140000clock, time:19.264167)
Finish normal[pid:12341] (9970000clock, time:29.298647)

プロンプトがちょこっと帰ってきたのでtopを一緒に表示。chrt終了後は普通に操作できた。

topコマンド見

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
12342 root      RT   0  3976  448  372 R 99.9  0.0   0:02.13 loop
12343 root       0 -20  3976  452  372 R  6.7  0.0   0:00.08 loop
    1 root      20   0 19272 1512 1248 S  0.0  0.1   0:01.38 init

(更新後...)

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
12343 root       0 -20  3976  452  372 R 70.1  0.0   0:01.49 loop
12341 root      20   0  3976  452  372 R  0.7  0.0   0:00.01 loop
    1 root      20   0 19272 1512 1248 S  0.0  0.1   0:01.38 init

chrtはPRがRTになってる。niceはPRが0でNIが-20。普通はPRが20でNIが0。

# ./kick.sh ./loop2
Start chrt[pid:12354]
Start nice[pid:12355]
Start normal[pid:12353]
Finish chrt[pid:12354] (0clock, time:78.973567)
Finish nice[pid:12355] (0clock, time:78.983294)
Finish normal[pid:12353] (0clock, time:78.990050)

論外


自宅鯖(nimono鯖)

# ./kick.sh ./loop
Start normal[pid:24110]
Start chrt[pid:24111]
Start nice[pid:24112]
Finish chrt[pid:24111] (10540000clock, time:10.550355)
Finish nice[pid:24112] (10580000clock, time:10.830323)
Finish normal[pid:24110] (10600000clock, time:21.227035)

プロンプトが全く帰ってこなかった。chrt終了後は普通に操作できた。

# ./kick.sh ./loop2
Start normal[pid:24196]
Start chrt[pid:24197]
Start nice[pid:24198]
Finish chrt[pid:24197] (100000clock, time:40.157387)
Finish normal[pid:24196] (100000clock, time:41.892862)
Finish nice[pid:24198] (100000clock, time:41.924659)

期待通りだが、clock数がどれも一緒なのが怪しい。

まとめ

  • 処理速度を上げる効果はない。(clock数はどれも優先度に限らず、ばらばら。)
  • CPUが取り合いになっている状態のときは、chrt>nice>普通の順で優先度が守られているのが分かった。
    • CPUが多めに割り当てられるので、実行時間が短くなる。
  • スリープ(待ち状態)が頻繁に発生すると、優先度の効果が小さくなる。

loop1.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>

double timevaldiff(const struct timeval *et, const struct timeval *st){
        return (et->tv_sec - st->tv_sec) * 1000000.0 + (et->tv_usec - st->tv_usec) ;
}

int main(int argc, char **argv){
        unsigned int loop = 4000000000;
        unsigned int i;
        pid_t pid = getpid();

        clock_t sc, ec;
        struct timeval st, et;

        if(argc != 2){
                printf("usage:%s [NAME]", argv[0]);
                exit(1);
        }

        printf("Start %s[pid:%d]\n", argv[1], pid);

        sc = clock();
        gettimeofday(&st, NULL);
        for(i=0;i<loop;++i);
        gettimeofday(&et, NULL);
        ec = clock();

        printf("Finish %s[pid:%d] (%dclock, time:%f)\n", argv[1], pid, ec-sc, timevaldiff(&et, &st)/1000000);

        return 0;
}

loop2.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>

double timevaldiff(const struct timeval *et, const struct timeval *st){
        return (et->tv_sec - st->tv_sec) * 1000000.0 + (et->tv_usec - st->tv_usec) ;
}

int main(int argc, char **argv){
        unsigned int loop = 40000;
        unsigned int i;
        pid_t pid = getpid();

        clock_t sc, ec;
        struct timeval st, et;

        if(argc != 2){
                printf("usage:%s [NAME]", argv[0]);
                exit(1);
        }

        printf("Start %s[pid:%d]\n", argv[1], pid);

        sc = clock();
        gettimeofday(&st, NULL);
        for(i=0;i<loop;++i) usleep(1000);
        gettimeofday(&et, NULL);
        ec = clock();

        printf("Finish %s[pid:%d] (%dclock, time:%f)\n", argv[1], pid, ec-sc, timevaldiff(&et, &st)/1000000);

        return 0;
}

kick.sh

#!/bin/sh

if [ $# != 1 ] ;then
        echo "usage: $0 [COMMAND]";
        exit 1
fi

$1 normal &
chrt -r 99 $1 chrt &
/bin/nice -n -20 $1 nice &