合計でのオーバーフローの検出


8

私はの配列を指定していたとの固定幅の整数を(彼らは幅のレジスタに収まるすなわち)、。2の補数演算を備えたマシンで合計を計算します。これは、ラップアラウンドセマンティクスでを法とする加算を実行します。それは簡単ですが、合計がレジスタサイズをオーバーフローする可能性があり、オーバーフローすると、結果が不正になります。nwa1,a2,anS=a1++an2w

合計がオーバーフローしない場合は、それを計算し、オーバーフローがないことをできるだけ早く確認したいと考えています。合計がオーバーフローした場合、それがオーバーフローしていることだけを知りたいので、値は気にしません。

部分的な合計がオーバーフローする可能性があるため、単純に順番に数値を追加することはできません。たとえば、8ビットレジスタでは、は有効であり、合計がですが、部分合計がレジスタ範囲オーバーフローします。(120,120,115)125120+120[128,127]

明らかに、より大きなレジスタをアキュムレータとして使用することもできますが、可能な限り最大のレジスタサイズをすでに使用している興味深いケースを想定してみましょう。

現在の部分合計とは逆の符号を持つ数値追加するよく知られた手法があります。この手法は、キャッシュに優しくなく、分岐予測や投機的実行をあまり活用しないという犠牲を払って、すべてのステップでオーバーフローを回避します。

おそらく部分合計をオーバーフローする権限を利用し、オーバーフローフラグ、キャッシュ、分岐予測子、および投機的実行とロードを備えた一般的なマシンでより高速な手法はありますか?

(これは、オーバーフローの安全な合計のフォローアップです)


Daveのソリューションがキャッシュとパイプラインでうまく機能しないのはなぜですか?仮想ピボットを使用したインプレースQuicksortパーティショニングと同様のことを行う場合、パーティショニングと次の集計の両方でキャッシュを適切に処理します。パーティショニング中のブランチの予測ミスについては知りませんが、その点でも加算フェーズはうまくいくはずです。0
ラファエル

@Raphael私のアプリケーションでは、オーバーフローは例外的なケースです。「これはオーバーフローしますか?」に対応する条件文 したがって、分岐予測によって十分に機能します。「この数値は正ですか?」に対応する条件文 予測できません。カーソルが1つではなく2つあるため、キャッシュ効果は確かにわずかです。
Gilles 'SO-邪悪なことをやめなさい'

回答:


3

を追加できますn個のサイズをますw 使用している場合はオーバーフローなし ログ+wビット演算。私の提案は、それだけを行い、結果が範囲内にあるかどうかを確認することです。倍精度演算のアルゴリズムはよく知られており(参照が必要な場合はTAOCPセクション4.3を参照)、ハードウェアによる加算(キャリーフラグおよびキャリー命令を使用した加算)がしばしばサポートされます。このようなサポートがなくても、データ依存ジャンプなしで実装できます(これはジャンプ予測子に適しています)。データを1回パスするだけで、最も便利な順序でデータにアクセスできます(キャッシュに適しています)。

データがメモリに収まらない場合、制限要因はIOであり、IOと計算のオーバーラップにどれだけ成功するかです。

データがメモリに収まる場合は、おそらく ログw(私が考えることができる唯一の例外は、通常64Kのメモリを備えた8ビットのマイクロプロセッサです)これは、倍精度演算を実行していることを意味します。ループ実行のオーバーヘッドw-ビット演算は、2つの命令(1つは符号拡張、もう1つはキャリーで加算)とレジスター圧力のわずかな増加(ただし、私が正しい場合、レジスターが不足しているx86でも十分なレジスターがあり、唯一のメモリアクセスが可能です)内部ループでデータをフェッチできます)。OOプロセッサがメモリロードレイテンシ中に追加の操作をスケジュールできるようになると思われるので、内部ループはメモリ速度で実行されます。したがって、この演習では、利用可能な帯域幅(プリフェッチ)の使用を最大化します。またはインターリーブ手法は、メモリアーキテクチャによっては役立つ場合があります)。

最新のポイントを考えると、より優れたパフォーマンスを持つ他のアルゴリズムを考えるのは困難です。データに依存する(したがって予測できない)ジャンプは、データのいくつかのパスと同様に問題外です。今日のプロセッサのいくつかのコアを使用しようとしても、メモリ帯域幅がおそらく飽和するので難しいでしょうが、インターリーブアクセスを実装する簡単な方法かもしれません。


マシンのレジスターのサイズを大きくできません。可能な最大のレジスタサイズをすでに使用していると仮定します。
Gilles「SO-悪をやめる」

@Gilles、私が知っているオーバーフローフラグを持っているプロセッサには、キャリー命令とキャリー命令付き加算があります。しない(MIPS以外の何か)場合でも、多精度演算は重要な候補になります(データのパスは1つだけです-キャッシュに適しています-順次アクセスします-キャッシュプレフィラーに適しています- -そして、データ依存ジャンプなしで実装できます-ジャンプ予測に適しています)。
AProgrammer 2012

「多重精度演算」とはどういう意味ですか?あなたは浮動小数点を意味すると思った。しかし、多くのアーキテクチャには、存在するとしても十分な大きさの浮動小数点レジスタがありません。amd64に64ビット整数、またはVFPなしのARMに32ビット整数を追加するとします。
Gilles「SO-邪悪なことをやめよ」

@ギレス、TAOCPのセクション4.3で説明されていることを意味しました。1つの単語では保持できない値を表すために複数の単語を使用することです。Bignumは、単語数が動的に調整されるバリアントです。私の推測では、ここで必要な単語数の最大範囲を決定できます(つまり、データがメモリにある場合は2、そうでない場合は、計算を伴うIOは最初のアクションポイントであり、IOバウンドになります)そしてそれを使用するだけです。それは十分に低く、さまざまな数の単語の処理がよりコストがかかります。
AProgrammer 2012

あ、そう。あなたの答えでこれを明確にできますか?タイミングに関するリファレンスや他の方法との比較はありますか?
Gilles「SO-邪悪なことをやめなさい」

1

整数型が抽象代数環として動作するマシン(基本的にはラップすることを意味します)では、item [i]と(item [i] >> 16)の合計を最大約32767個のアイテムについて計算できます。最初の値は、正しい合計の下位32ビットを示します。後者の値は、正しい合計に近いもののビット16〜47を生成し、前者の値を使用すると、正確な正しい合計のビット16〜47を生成するように簡単に調整できます。

擬似コードは次のようになります。

Sum1=0 : Sum2 = 0
For up to 32768 items L[i] in list
  Sum1 = Sum1 +L[i]
  Sum2 = Sum2 +(L[i] >> 16) ' Use sign-extending shift
Loop
Sum1MSB = Sum1 >> 16 ' Cannot use division of numbers can be negative--see below
Sum2Mid = Sum2 and 65535
Sum2Adj = Sum1MSB - Sum2Mid
If Sum2Adj >= 32768 then Sum2Adj = Sum2Adj - 65536
Sum2 += Sum2Adj

上記のコードの後、Sum2とSum1は、間にあるオーバーフローに関係なく、正しい合計を生成するはずです。32768を超える数を合計する必要がある場合は、それらを32768のグループに分割し、各グループのSum2を計算した後、それをすべてのグループ全体の2変数「ビッグサム」に追加できます。

一部の言語では、右シフト演算子を65536による除算で置き換えることができます。これは、Sum2を計算するときに一般に機能しますが、Sum1MSBを抽出するときには機能しません。問題は、一部の言語が除算をゼロに向かって丸める一方で、ここで除算の丸めを次に低い数値に(負の無限大に向かって)実行する必要があることです。Sum2の計算エラーは後で修正されますが、Sum2LSBの計算エラーは最終結果に影響します。

最終結果には、Sum1に関連する計算のいずれかが「オーバーフロー」したかどうかは示されませんが、値が完全にラップされることが保証されている場合、コードはオーバーフローが発生したかどうかを気にする必要はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.