共用方式為


最佳化檔案存取

建立可有效存取檔案系統的通用 Windows 平台 (UWP) 應用程式,避免因為磁碟延遲和記憶體/CPU 週期而發生效能問題。

當您想要存取大量檔案,並且想要存取一般 Name、FileType 和 Path 屬性以外的屬性值時,請建立 QueryOptions 並呼叫 SetPropertyPrefetch 來存取這些檔案。 SetPropertyPrefetch 方法可以大幅改善顯示從檔案系統所取得項目集合的應用程式效能,例如影像集合。 下一組範例示範幾個存取多個檔案的方式。

第一個範例會使用 Windows.Storage.StorageFolder.GetFilesAsync 來擷取一組檔案的名稱資訊。 此方法提供良好的效能,因為範例只會存取 name 屬性。

StorageFolder library = Windows.Storage.KnownFolders.PicturesLibrary;
IReadOnlyList<StorageFile> files = await library.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByDate);

for (int i = 0; i < files.Count; i++)
{
    // do something with the name of each file
    string fileName = files[i].Name;
}
Dim library As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
Dim files As IReadOnlyList(Of StorageFile) =
    Await library.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByDate)

For i As Integer = 0 To files.Count - 1
    ' do something with the name of each file
    Dim fileName As String = files(i).Name
Next i

第二個範例會使用 Windows.Storage.StorageFolder.GetFilesAsync,然後擷取每個檔案的映像屬性。 此方法提供效能不佳。

StorageFolder library = Windows.Storage.KnownFolders.PicturesLibrary;
IReadOnlyList<StorageFile> files = await library.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByDate);
for (int i = 0; i < files.Count; i++)
{
    ImageProperties imgProps = await files[i].Properties.GetImagePropertiesAsync();

    // do something with the date the image was taken
    DateTimeOffset date = imgProps.DateTaken;
}
Dim library As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
Dim files As IReadOnlyList(Of StorageFile) = Await library.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByDate)
For i As Integer = 0 To files.Count - 1
    Dim imgProps As ImageProperties =
        Await files(i).Properties.GetImagePropertiesAsync()

    ' do something with the date the image was taken
    Dim dateTaken As DateTimeOffset = imgProps.DateTaken
Next i

第三個範例會使用 QueryOptions 來取得一組檔案的相關資訊。 此方法提供比上一個範例更好的效能。

// Set QueryOptions to prefetch our specific properties
var queryOptions = new Windows.Storage.Search.QueryOptions(CommonFileQuery.OrderByDate, null);
queryOptions.SetThumbnailPrefetch(ThumbnailMode.PicturesView, 100,
        ThumbnailOptions.ReturnOnlyIfCached);
queryOptions.SetPropertyPrefetch(PropertyPrefetchOptions.ImageProperties, 
       new string[] {"System.Size"});

StorageFileQueryResult queryResults = KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(queryOptions);
IReadOnlyList<StorageFile> files = await queryResults.GetFilesAsync();

foreach (var file in files)
{
    ImageProperties imageProperties = await file.Properties.GetImagePropertiesAsync();

    // Do something with the date the image was taken.
    DateTimeOffset dateTaken = imageProperties.DateTaken;

    // Performance gains increase with the number of properties that are accessed.
    IDictionary<String, object> propertyResults =
        await file.Properties.RetrievePropertiesAsync(
              new string[] {"System.Size" });

    // Get/Set extra properties here
    var systemSize = propertyResults["System.Size"];
}
' Set QueryOptions to prefetch our specific properties
Dim queryOptions = New Windows.Storage.Search.QueryOptions(CommonFileQuery.OrderByDate, Nothing)
queryOptions.SetThumbnailPrefetch(ThumbnailMode.PicturesView,
            100, Windows.Storage.FileProperties.ThumbnailOptions.ReturnOnlyIfCached)
queryOptions.SetPropertyPrefetch(PropertyPrefetchOptions.ImageProperties,
                                 New String() {"System.Size"})

Dim queryResults As StorageFileQueryResult = KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(queryOptions)
Dim files As IReadOnlyList(Of StorageFile) = Await queryResults.GetFilesAsync()


For Each file In files
    Dim imageProperties As ImageProperties = Await file.Properties.GetImagePropertiesAsync()

    ' Do something with the date the image was taken.
    Dim dateTaken As DateTimeOffset = imageProperties.DateTaken

    ' Performance gains increase with the number of properties that are accessed.
    Dim propertyResults As IDictionary(Of String, Object) =
        Await file.Properties.RetrievePropertiesAsync(New String() {"System.Size"})

    ' Get/Set extra properties here
    Dim systemSize = propertyResults("System.Size")

Next file

如果您要在 Windows.Storage 物件上執行多個作業,例如 Windows.Storage.ApplicationData.Current.LocalFolder,請建立區域變數來參考該儲存體來源,如此一來,您就不會在每次存取時重新建立中繼物件。

C# 與 Visual Basic 中的串流效能

UWP 與 .NET 串流之間的緩衝

您在許多案例中可能需要將 UWP 串流 (例如 Windows.Storage.Streams.IInputStreamIOutputStream) 轉換成 .NET 串流 (System.IO.Stream)。 例如,當您撰寫 UWP 應用程式,並想要使用現有的 .NET 程式碼搭配 UWP 檔案系統處理串流時,這會很有用。 若要啟用此功能,適用於 UWP 應用程式的 .NET API 會提供延伸方法,讓您在 .NET 與 UWP 資料流類型之間進行轉換。 如需詳細資訊,請參閱 WindowsRuntimeStreamExtensions

當您將 UWP 串流轉換成 .NET 串流時,您可以有效地建立基礎 UWP 串流的配接器。 在某些情況下,與在 UWP 串流上叫用方法相關聯的執行階段成本。 這可能會影響應用程式的速度,特別是在您執行許多小型、頻繁讀取或寫入作業的案例。

為了加速應用程式,UWP 串流配接器包含資料緩衝區。 下列程式碼範例示範使用具有預設緩衝區大小的 UWP 串流配接器進行小型連續讀取。

StorageFile file = await Windows.Storage.ApplicationData.Current
    .LocalFolder.GetFileAsync("example.txt");
Windows.Storage.Streams.IInputStream windowsRuntimeStream = 
    await file.OpenReadAsync();

byte[] destinationArray = new byte[8];

// Create an adapter with the default buffer size.
using (var managedStream = windowsRuntimeStream.AsStreamForRead())
{

    // Read 8 bytes into destinationArray.
    // A larger block is actually read from the underlying 
    // windowsRuntimeStream and buffered within the adapter.
    await managedStream.ReadAsync(destinationArray, 0, 8);

    // Read 8 more bytes into destinationArray.
    // This call may complete much faster than the first call
    // because the data is buffered and no call to the 
    // underlying windowsRuntimeStream needs to be made.
    await managedStream.ReadAsync(destinationArray, 0, 8);
}
Dim file As StorageFile = Await Windows.Storage.ApplicationData.Current -
.LocalFolder.GetFileAsync("example.txt")
Dim windowsRuntimeStream As Windows.Storage.Streams.IInputStream =
    Await file.OpenReadAsync()

Dim destinationArray() As Byte = New Byte(8) {}

' Create an adapter with the default buffer size.
Dim managedStream As Stream = windowsRuntimeStream.AsStreamForRead()
Using (managedStream)

    ' Read 8 bytes into destinationArray.
    ' A larger block is actually read from the underlying 
    ' windowsRuntimeStream and buffered within the adapter.
    Await managedStream.ReadAsync(destinationArray, 0, 8)

    ' Read 8 more bytes into destinationArray.
    ' This call may complete much faster than the first call
    ' because the data is buffered and no call to the 
    ' underlying windowsRuntimeStream needs to be made.
    Await managedStream.ReadAsync(destinationArray, 0, 8)

End Using

在將 UWP 串流轉換成 .NET 串流的大部分案例中,這都是理想的預設緩衝行為。 不過,在某些情況下,您可能需要調整緩衝行為,以提高效能。

使用大型資料集

讀取或寫入較大的資料集時,您可以向 AsStreamForReadAsStreamForWriteAsStream 擴充方法提供大型緩衝區大小,以增加讀取或寫入輸送量。 這會為串流配接器提供較大的內部緩衝區大小。 例如,將來自大型檔案的串流傳遞至 XML 剖析器時,剖析器可以從串流進行許多循序小型讀取。 大型緩衝區可以減少對基礎 UWP 串流的呼叫數目,並提升效能。

注意 在設定大於約 80 KB 的緩衝區大小時,請務必謹慎,因為這可能導致記憶體回收行程堆積分散 (請參閱改善記憶體回收效能)。 下列程式碼範例會建立具有 81,920 位元組緩衝區的受控串流配接器。

// Create a stream adapter with an 80 KB buffer.
Stream managedStream = nativeStream.AsStreamForRead(bufferSize: 81920);
' Create a stream adapter with an 80 KB buffer.
Dim managedStream As Stream = nativeStream.AsStreamForRead(bufferSize:=81920)

Stream.CopyToCopyToAsync 方法也會配置本機緩衝區,以在串流之間複製。 如同 AsStreamForRead 擴充方法,您可以覆寫預設緩衝區大小,以取得大型串流複本的較佳效能。 下列程式碼範例示範如何變更 CopyToAsync 呼叫的預設緩衝區大小。

MemoryStream destination = new MemoryStream();
// copies the buffer into memory using the default copy buffer
await managedStream.CopyToAsync(destination);

// Copy the buffer into memory using a 1 MB copy buffer.
await managedStream.CopyToAsync(destination, bufferSize: 1024 * 1024);
Dim destination As MemoryStream = New MemoryStream()
' copies the buffer into memory using the default copy buffer
Await managedStream.CopyToAsync(destination)

' Copy the buffer into memory using a 1 MB copy buffer.
Await managedStream.CopyToAsync(destination, bufferSize:=1024 * 1024)

此範例使用 1 MB 的緩衝區大小,其大於先前建議的 80 KB。 使用這類大型緩衝區可以改善非常大型資料集的複製作業輸送量 (也就是數百 MB)。 不過,此緩衝區會配置在大型物件堆積上,且可能會降低廢棄項目收集效能。 只有在大幅改善應用程式的效能時,才應該使用大型緩衝區大小。

當您同時使用大量串流時,可能會需要減少或消除緩衝區的記憶體額外負荷。 您可以指定較小的緩衝區,或將 bufferSize 參數設定為 0,以完全關閉該串流配接器的緩衝處理。 如果您對受控串流執行大型讀取和寫入,仍可以在不需要緩衝處理的情況下達到良好的輸送量效能。

執行延遲敏感性作業

如果您想要低延遲讀取和寫入,且不想在基礎 UWP 串流外的大型區塊中讀取,則也可以避免緩衝處理。 例如,如果您使用串流進行網路通訊,可能會需要要低延遲讀取和寫入。

在聊天應用程式中,您可以使用透過網路介面的串流來回傳送訊息。 在此情況下,您需要在訊息就緒後立即傳送訊息,而不是等待緩衝區填滿。 如果您在呼叫 AsStreamForReadAsStreamForWriteAsStream 擴充方法時將緩衝區大小設定為 0,則產生的配接器將不會配置緩衝區,且所有呼叫都會直接操作基礎 UWP 串流。