2015-01-09 22 views
19

実行時にRustで決定されたサイズのスタック割り当て配列を持つことは可能ですか?Rustのalloca/variable length配列ですか?

void go(int n) { 
    int array[n]; 
    // ... 
} 
+0

あなたは常にCからのalloca関数をインポートすることができます。何らかのオーバーフローチェックを追加すると、それを安全にすることさえできるかもしれません。 –

+4

@ker:そんなことはありません。 'alloca'は実際には関数ではありませんが、それはコンパイラ固有のコンパイラです。少なくともLLVMでは、実際のIR命令なので、直接インポートする方法はありません。インラインIRを書くことができる必要がありますが、現時点では不可能です。 –

+0

私はそれらの人たちが何をインポートしたのか不思議に思っています:https://github.com/tomaka/cpal/blob/master/alsa-sys/src/lib.rs#L2135 ...私はそれが今のAC標準機能だと確信していましたhttp://man7.org/linux/man-pages/man3/alloca.3.html) –

答えて

18

のように、それをサポートする言語で直接構文がないため、可能ではありません。言われて、C99のこの特定の機能は議論の余地がある、それは特定の利点(mallocをバイパスキャッシュの局所&)を持っていますが、それは欠点も(ブローアップしてスタックを簡単に、切り株の最適化の数を持って、静的回すこと

動的オフセットへのオフセット、...)。

今のところ、Vecを使用することをおすすめします。パフォーマンスの問題がある場合は、いわゆる「スモールベクトル最適化」を調べることができます。私は定期的にパフォーマンスは(Cで)以下のパターンを必要とされるコードで見てきた:

enum InlineVector<T> { 
    Inline(usize, [T; 64]), 
    Dynamic(Vec<T>), 
} 

:今

SomeType array[64] = {}; 
SomeType* pointer, *dynamic_pointer; 
if (n <= 64) { 
    pointer = array; 
} else { 
    pointer = dynamic_pointer = malloc(sizeof(SomeType) * n); 
} 

// ... 

if (dynamic_pointer) { free(dynamic_pointer); } 

、これは錆が(方法で、より良い)を簡単にサポートして何かがあります下の簡単な実装例を見ることができます。

  • がスタックを爆破を避けるために、64の未満の要素はそうでないヒープを切っ
  • 移動を必要としているスタックを使用しています。

    は何の問題は、しかし、あなたは今のタイプを持っているということです

もちろん、常には2を使用する場合でもスタック上に64個の要素のための十分な領域を確保します。しかし、代わりにallocaへの呼び出しはありません。そのため、バリアントに動的なオフセットがあるという問題を回避できます。

Cとは逆に、機能の外でスタック割り当て配列への参照を誤って返すことがないように、生涯追跡の恩恵を受けることができます。

注:本格的な実装では、型のないパラメータが必要になるため、64をカスタマイズすることはできますが、まだRustは存在しません。


私はほとんどの "明白な" 方法を披露する予定だ。

impl<T: Copy + Clone> InlineVector<T> { 
    fn new(v: T, n: usize) -> InlineVector<T> { 
     if n <= 64 { 
      InlineVector::Inline(n, [v; 64]) 
     } else { 
      InlineVector::Dynamic(
       FromIterator::from_iter(std::iter::repeat(v).take(n)) 
      ) 
     } 
    } 

    fn len(&self) -> usize { 
     match self { 
      &InlineVector::Inline(n, _) => n, 
      &InlineVector::Dynamic(ref vec) => vec.len(), 
     } 
    } 

    fn as_slice(&self) -> &[T] { 
     match self { 
      &InlineVector::Inline(n, ref array) => array.as_slice().slice_to(n), 
      &InlineVector::Dynamic(ref vec) => vec.as_slice(), 
     } 
    } 

    fn as_mut_slice(&mut self) -> &mut [T] { 
     match self { 
      &mut InlineVector::Inline(n, ref mut array) => 
       array.as_mut_slice().slice_to_mut(n), 
      &mut InlineVector::Dynamic(ref mut vec) => 
       vec.as_mut_slice(), 
     } 
    } 
} 

使用法:

fn main() { 
    let mut v = InlineVector::new(1u32, 4); 
    v.as_mut_slice()[2] = 3; 
    println!("{}: {}", v.len(), v.as_slice()[2]) 
} 

予想通り4: 3を印刷します。参照、必要な輸入/微調整のために

#![allow(unstable)] 

use std::iter::FromIterator; 
use std::vec::Vec; 
+1

[smallvec](https://crates.io/crates/smallvec)の箱がこれを実装しています。 – malbarbo

+0

@malbarbo:確かに、タイプレベルの整数をスマートに持たないという問題も起こります!私は、* array *型をユーザに指定して、サイズを指定させることを考えなかった。 –

2

ルーストに上[i32]ようDSTS(動的にサイズタイプ)を保存する機能を必要とすることを行う:

私は、次のC99コードの同等を探していますスタックでは、言語がサポートしていません。

もっと深い理由は、私の知るところでは、LLVMはこれを本当にサポートしていないということです。私はあなたがすることができますが、それははかなりが最適化を妨げると信じて与えられています。このように、私はこれを可能にする近い将来の計画を知らない。

+12

LLVMは、まず第一にC/C++オプティマイザなので、比較的よくサポートされています。 – huon

+1

私はそれが育ったのを見るたびに、ブロック内でスタックのサイズを動的に変更すると、最適化を行うLLVMの能力が著しく損なわれると述べています。私はそれを支持するか否定する確かな証拠を見つけたことはありません。だから、私は聞いたことのないものを多かれ少なかれ進んでいます。私はこのことについて間違っていることを愛していますが、スタックDSTは面白いでしょう。 :) –

+0

私は「オプティマイザに干渉している」と思います。これは、ダイナミックスタック割り当てのやや固有の性質です。 (それは、LLVM開発者がうまく処理するために多大な努力を払わなかったことかもしれません) – huon

関連する問題