はじめに
備忘録のようなものは需要がないと思いますが, 少しの価値はありそうなので.
Epic Gamesに直接要望だせばと思われるかもしれませんが, 明らかな不具合でない限り改善されるかどうか不確かですし, そもそも直るとも思えないので改造してしまった方が早いでしょう.
マテリアルのパラメータ更新
UEのマテリアルはランタイムではイミュータブルになります. これを更新可能にするには, マテリアルからUMaterialInstanceDynamic
を作成します. UMaterialInstanceDynamic
がパラメータを上書きするプロキシになるわけです.
このUMaterialInstanceDynamic
を通してパラメータ更新をするとき, 現在のパラメータ値と異なる値で更新しようとしたとき, 最後はMaterialInstanceSupport.h
のFMaterialInstanceResource::RenderThread_FindParameterByNameInternal
に到達します.
ここが5.03
以降のボトルネックになります.
5.03
問題はなさそうとはいえ, なんでint
使ってんだなどのツッコミどころはいろいろありますが, なんとなくカッコイイアルゴリズムのように見えます.
template <typename ValueType>
int RenderThread_FindParameterByNameInternal(const FHashedMaterialParameterInfo& ParameterInfo, bool& OutWasFound) const
{
const TArray<TNamedParameter<ValueType> >& ValueArray = GetValueArray<ValueType>();
TNamedParameter<ValueType> SearchParam;
SearchParam.Info = ParameterInfo;
int Index = Algo::LowerBound(ValueArray, SearchParam,
[](const TNamedParameter<ValueType>& Left, const TNamedParameter<ValueType>& Right)
{
return GetTypeHash(Left.Info) < GetTypeHash(Right.Info);
});
uint32 SearchHash = GetTypeHash(ParameterInfo);
while (ValueArray.IsValidIndex(Index) && GetTypeHash(ValueArray[Index].Info) == SearchHash)
{
if (ValueArray[Index].Info == ParameterInfo)
{
OutWasFound = true;
return Index;
}
Index++;
}
OutWasFound = false;
return Index;
}
問題なのはGetTypeHash
で, FHashedMaterialParameterInfo
のハッシュ計算には, GetTypeHash(FName)
とHashCombine(uint32,uint32)
が2回必要です. GetTypeHash(FName)
は, 加算4回+シフト3回, HashCombine(uint32,uint32)
は, 加算1回+減算18回+排他論理和9回+シフト9回です.
GetTypeHash(FHashedMaterialParameterInfo)
は, 加算6回+減算36回+排他論理和18回+シフト21回になります.
ハッシュ値計算やデータ構造の複雑さからくるオーバーヘッドの実験はこちら, STLでFNVを使用している場合やはりハッシュ値計算は遅いです.
小さいサイズの辞書は線形探索でいいかも
4.XX以前
更新されるパラメータが数百個に及ばない限り, 何もする必要がなかったのです. 線形探索しているだけなので遅いように見えますが, FHashedMaterialParameterInfo
の比較は高々整数の比較4回なので, 多くの環境でハッシュ値を計算している間に数十個の要素を比較できることでしょう.
TArray<TNamedParameter<ValueType> >& ValueArray = GetValueArray<ValueType>();
const int32 ParameterCount = ValueArray.Num();
for (int32 ParameterIndex = 0; ParameterIndex < ParameterCount; ++ParameterIndex)
{
TNamedParameter<ValueType>& Parameter = ValueArray[ParameterIndex];
if (Parameter.Info == ParameterInfo)
{
Parameter.Value = Value;
return;
}
}
5.1以降
やはりまずいと思ったのか, 大改造されました. TMap
ではなく, ここだけの専用のハッシュマップとハッシュ関数に変わりました. 計測はしていませんが, 数十のパラメータ更新ならまだ線形探索の方が速いでしょう.
まとめ
ハッシュマップからの検索には, ハッシュ値の計算が最低でも1回必要ということが知られていないと思います. 線形探索した方が速いことはよくあります.