【发布时间】:2020-11-12 03:11:36
【问题描述】:
我发现了一个 VirtualizingWrapPanel(VWP) 项目here,我认为它可以帮助我优化我的 ListView 滚动性能。 listView 必须有四列和多行才能显示源项目。所以我尝试使用这个 VWP,但是滚动(我把它做成 DoubleAnimation)平滑度仍然很差,帧率下降到大约 30 fps 而且非常明显。我从上面的链接中复制了源代码来尝试调试它并了解性能如此糟糕的原因。
几个小时后发现VWP的ArrangeOverride方法和它的“继承父”的MeasureOverride方法" 可以执行大约 20-30ms (1000ms / 30ms = ~30fps) 就是这样,我找到了它这么慢的原因......但是为什么呢?
MeasureOverride 方法调用两个可能很重的方法:RealizeItems 和 VirtualizeItems
protected virtual void RealizeItems()
{
var startPosition = ItemContainerGenerator.GeneratorPositionFromIndex(ItemRange.StartIndex);
int childIndex = startPosition.Offset == 0 ? startPosition.Index : startPosition.Index + 1;
using (ItemContainerGenerator.StartAt(startPosition, GeneratorDirection.Forward, true))
{
for (int i = ItemRange.StartIndex; i <= ItemRange.EndIndex; i++, childIndex++)
{
UIElement child = (UIElement)ItemContainerGenerator.GenerateNext(out bool isNewlyRealized);
if (isNewlyRealized || /*recycled*/!InternalChildren.Contains(child))
{
if (childIndex >= InternalChildren.Count)
{
AddInternalChild(child);
}
else
{
InsertInternalChild(childIndex, child);
}
ItemContainerGenerator.PrepareItemContainer(child);
child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
}
if (child is IHierarchicalVirtualizationAndScrollInfo groupItem)
{
groupItem.Constraints = new HierarchicalVirtualizationConstraints(
new VirtualizationCacheLength(0),
VirtualizationCacheLengthUnit.Item,
new Rect(0, 0, ViewportWidth, ViewportHeight));
child.Measure(new Size(ViewportWidth, ViewportHeight));
}
}
}
}
protected virtual void VirtualizeItems()
{
for (int childIndex = InternalChildren.Count - 1; childIndex >= 0; childIndex--)
{
var generatorPosition = GetGeneratorPositionFromChildIndex(childIndex);
int itemIndex = ItemContainerGenerator.IndexFromGeneratorPosition(generatorPosition);
if (!ItemRange.Contains(itemIndex))
{
if (VirtualizationMode == VirtualizationMode.Recycling)
{
ItemContainerGenerator.Recycle(generatorPosition, 1);
}
else
{
ItemContainerGenerator.Remove(generatorPosition, 1);
}
RemoveInternalChildRange(childIndex, 1);
}
}
}
所以我知道开发人员可能会在代码中犯错,我也知道可能没有一个可以优化的东西,但我真的不明白为什么它这么慢..循环迭代大约 40- 50 个 UIElement 对象,每个对象都有两个子对象(TextBlock 和 ImageSource),这并不是一个庞大的对象数量..
我想在这些循环中使用并行任务,但 UIElement 和其他不是线程安全并且只能在UI 线程...或可以访问?
如何提高这些方法的性能?可以实现高效的多线程吗? 谢谢!
【问题讨论】:
-
您不能并行化容器生成。容器必须在 UI 线程上创建。
-
是否有任何替代方案可以提高性能?
-
一般来说,您发布的代码看起来没问题。您可以通过检查例如检查是否真的有必要重新完成所有容器实现。如果传递给
MeasureOverride的约束大小已更改或超过某个阈值。基本上尝试优化或添加有助于避免冗余迭代或通过使用突破标准缩短它们的约束。在用户方面,尽量保持容器的可视化树尽可能小。删除所有未使用的元素,如边框和布局属性。检查缓存策略,现金是否太大或太小? -
也许分析负责在偏移更改上生成新项目的代码部分。从
SetHorizontalOffset和SetVerticalOffset开始。也许这里的代码生成了太多的项目,你找到了优化算法的方法。 -
很奇怪..我通过仅使用更改的项目进行操作来减少循环迭代次数,但几乎没有任何改变:(现在循环影响了几个(1-6)个对象,但是 child.Measure( ), child.Arrange() 和其他方法总共花费了太多时间..
标签: c# wpf multithreading performance ui-thread