シフト演算のパフォーマンス

先週の社内勉強会の話。IntegerをtoString()すると、StringのvalueOfに行って、結局、Integer.toString(int i)に戻ってくるんだったら、始めからtoString()の中でInteger.toString(i)を呼ばないのは無駄だね~、という話題もあったのだけど、それ以上に疑問に思った話題がシフト演算子と算術演算子で書いたときのパフォーマンス。まぁ、誤差っていえば誤差のような気もするのだけど、シフト演算子で書いたときの微妙な遅さ。比較に使ったコードは以下のような感じ。

package com.marevol.test;
public class App {
public static int dummy1 = 0;
public static int dummy2 = 0;
public static void main(String[] args) {
long max = 100000000L;
int value = 10;
// compile code dynamically
for (int i = 0; i < 5; i++) {
pattern1(max, value);
pattern2(max, value);
}
System.out.println("max=" + max);
long p1 = 0;
long p2 = 0;
for (int i = 0; i < 5; i++) {
p1 += pattern1(max, value);
p2 += pattern2(max, value);
}
System.out.println("Time(<<): " + p1 + " (" + dummy1 + ")");
System.out.println("Time(*X): " + p2 + " (" + dummy2 + ")");
}
public static long pattern1(long count, int num) {
long startTime = System.currentTimeMillis();
int c = 0;
for (long i = 0; i < count; i++) {
// c = (num << 6) + (num << 5) + (num << 2);
c = num << 1;
// c = num >> 1;
}
long endTime = System.currentTimeMillis();
dummy1 = c;
return endTime - startTime;
}
public static long pattern2(long count, int num) {
long startTime = System.currentTimeMillis();
int c = 0;
for (long i = 0; i < count; i++) {
// c = num * 100;
c = num * 2;
// c = num / 2;
}
long endTime = System.currentTimeMillis();
dummy2 = c;
return endTime - startTime;
}
}

これを実行すると以下みたいな感じ。

$ java -version
java version "1.6.0_06"
Java(TM) SE Runtime Environment (build 1.6.0_06-b02)
Java HotSpot(TM) 64-Bit Server VM (build 10.0-b22, mixed mode)
$ uname -r -m -v -s
Linux 2.6.18-53.1.19.el5 #1 SMP Wed May 7 08:22:53 EDT 2008 x86_64
$ cat /proc/cpuinfo |grep "model name"
model name      : Intel(R) Core(TM)2 CPU          6300  @ 1.86GHz
model name      : Intel(R) Core(TM)2 CPU          6300  @ 1.86GHz
$ java -cp target/classes/ com.marevol.test.App
max=100000000
Time(<<): 547 (20)
Time(*X): 543 (20)

これから思うに、シフト演算でがんばって計算しても意味ない気が・・・。微妙な差が気になるけど、JVMが実行しているコード見ないとわからん気も。

社内勉強会でjava.util.List

毎週、社内で勉強会を昼飯どきにやってますけど、今日は List についてああだ、こうだと話しました。まぁ、主に LinkedListとArrayListについてですけど、それぞれの特徴を考えればどう使えばいいとか、当たり前のことなのかもしれないけど、実際に JDK のソースコードを元に議論した。あれこれ話したけど、ArrayList で add したときに配列を越えていたらいくつ増やすのだろうとか、実際に見たりで 1.5 倍して +1 している感じでした。+1 って何だろうと考えたら、配列とか 1 とかだと 1.5 倍しても 1 だからだめじゃん、というわけの +1 ということで納得。実際の Java6 の対象のコードは、

public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
}

で、真ん中らへんの newCapacity のところがそれ。その他の部分については、minCapacity は最低限増やさなきゃならない配列数、oldCapacity は現在の配列数、newCapacityはこれくらい増やそうよ、という配列候補の配列数。で、newCapacityとminCapacity の比較している if 文は、addAll とかでがっつり配列が増やされるときにnewCapacity じゃ足りない場合があるから、必要ならminCapacityに変更している。ざっと見た感じではそんな感じかな(ちなみにmodCountはシリアライズとかするときに非同期に配列が書き換えられたときにチェックするための値。modCountがintを越える変更をしたらどうなるのだろう・・・)。さらに気になったので、Apache Harmonyもちらっと見たら、そもそもやりかたがちょっと違う。Harmony の方は、size を見るんじゃなくて、配列の先頭と最後の位置で管理していた。つまり、先頭の削除とかに強いのだろうね。という感じで、ちょっと賢くなりました。

日本茶売れ筋ランキング

N2ショッピングの母の日販売もひと段落して、Yahooショッピング内の日本茶売れ筋ランキングを見たら、10位に「茶倉特選ギフトセット SAKURA」がなっていたのに気がついた(私も親に茶倉のお茶を贈ったら美味しいと言われたので、今度、自分でも飲んでみないとな・・・。人に贈るばかりで自分でなかなか飲めない・・・)。

売れ筋商品ランキング: 10位
集計期間: 5/3~5/10
カテゴリ: 日本茶

今回の母の日販促は、顧客の動向を見つつ、いろいろと実験したりして学ぶことがあった(あまり実験しすぎると痛い目にあうのでほどほどにしたけど)。もっと上位にいけるような気がするので、今回の経験を活かして、お中元販促をがんばろっと。