为什么复制对字符串的引用比复制int要慢得多(但反之亦然,对于Array.Copy())?(Why is copying references to strings much slower than copying ints (but vice versa for Array.Copy())?)
假设我想将数组的一部分向右移动1.我可以使用
Array.Copy
或者只是Array.Copy
复制元素:private static void BuiltInCopy<T>(T[] arg, int start) { int length = arg.Length - start - 1; Array.Copy(arg, start, arg, start + 1, length); } private static void ElementByElement<T>(T[] arg, int start) { for (int i = arg.Length - 1; i > start; i--) { arg[i] = arg[i - 1]; } } private static void ElementByElement2<T>(T[] arg, int start) { int i = arg.Length - 1; while (i > start) arg[i] = arg[--i]; }
(
ElementByElement2
由Matt Howells建议。)我使用Minibench测试了它,结果让我感到非常惊讶。
internal class Program { private static int smallArraySize = 32; public static void Main(string[] args) { BenchArrayCopy(); } private static void BenchArrayCopy() { var smallArrayInt = new int[smallArraySize]; for (int i = 0; i < smallArraySize; i++) smallArrayInt[i] = i; var smallArrayString = new string[smallArraySize]; for (int i = 0; i < smallArraySize; i++) smallArrayString[i] = i.ToString(); var smallArrayDateTime = new DateTime[smallArraySize]; for (int i = 0; i < smallArraySize; i++) smallArrayDateTime[i] = DateTime.Now; var moveInt = new TestSuite<int[], int>("Move part of array right by 1: int") .Plus(BuiltInCopy, "Array.Copy()") .Plus(ElementByElement, "Element by element (for)") .Plus(ElementByElement2, "Element by element (while)") .RunTests(smallArrayInt, 0); var moveString = new TestSuite<string[], string>("Move part of array right by 1: string") .Plus(BuiltInCopy, "Array.Copy()") .Plus(ElementByElement, "Element by element (for)") .Plus(ElementByElement2, "Element by element (while)") .RunTests(smallArrayString, "0"); moveInt.Display(ResultColumns.All, moveInt.FindBest()); moveString.Display(ResultColumns.All, moveInt.FindBest()); } private static T ElementByElement<T>(T[] arg) { ElementByElement(arg, 1); return arg[0]; } private static T ElementByElement2<T>(T[] arg) { ElementByElement2(arg, 1); return arg[0]; } private static T BuiltInCopy<T>(T[] arg) { BuiltInCopy(arg, 1); return arg[0]; } private static void BuiltInCopy<T>(T[] arg, int start) { int length = arg.Length - start - 1; Array.Copy(arg, start, arg, start + 1, length); } private static void ElementByElement<T>(T[] arg, int start) { for (int i = arg.Length - 1; i > start; i--) { arg[i] = arg[i - 1]; } } private static void ElementByElement2<T>(T[] arg, int start) { int i = arg.Length - 1; while (i > start) arg[i] = arg[--i]; } }
请注意,这里没有测量分配。 所有方法都只是复制数组元素。 由于我在32位操作系统上,因此
int
和string
引用在堆栈上占用相同的空间量。这是我期望看到的:
BuiltInCopy
应该是最快的,原因有两个:1)它可以做内存复制; 2)List<T>.Insert
使用Array.Copy
。 另一方面,它是非泛型的,当数组有不同的类型时它可以做很多额外的工作,所以也许它没有充分利用1)。- 对于
int
和string
ElementByElement
应该同样快。- 对于
int
和string
,BuiltInCopy
要么同样快,要么对int
要慢一些(如果它必须做一些拳击)。但是,所有这些假设都是错误的(至少在我的.NET 3.5 SP1机器上)!
- 对于32个元素的数组,
BuiltInCopy<int>
明显慢于BuiltInCopy<int>
。 当大小增加时,BuiltInCopy<int>
变得更快。ElementByElement<string>
比ElementByElement<int>
慢4倍 。BuiltInCopy<int>
比BuiltInCopy<string>
更快。任何人都可以解释这些结果吗?
更新:从CLR代码生成团队博客文章中关于数组边界检查消除 :
建议4:当您复制中型到大型数组时,请使用Array.Copy,而不是显式复制循环。 首先,所有范围检查将被“提升”到循环外的单个检查。 如果数组包含对象引用,您还将有效地“提升”与存储到对象类型数组相关的两个额外费用:与数组协方差相关的每个元素“存储检查”通常可以通过检查动态来消除数组的类型和与垃圾收集相关的写屏障将被聚合并变得更加有效。 最后,我们将能够使用更高效的“memcpy”式复制循环。 (在即将到来的多核世界中,如果阵列足够大,甚至可能采用并行性!)
最后一列是得分(以刻度/迭代次数表示的总持续时间,由最佳结果标准化)。
两次运行在
smallArraySize = 32
:f:\MyProgramming\TimSort\Benchmarks\bin\Release>Benchmarks.exe ============ Move part of array right by 1: int ============ Array.Copy() 468791028 0:30.350 1,46 Element by element (for) 637091585 0:29.895 1,06 Element by element (while) 667595468 0:29.549 1,00 ============ Move part of array right by 1: string ============ Array.Copy() 432459039 0:30.929 1,62 Element by element (for) 165344842 0:30.407 4,15 Element by element (while) 150996286 0:28.399 4,25 f:\MyProgramming\TimSort\Benchmarks\bin\Release>Benchmarks.exe ============ Move part of array right by 1: int ============ Array.Copy() 459040445 0:29.262 1,38 Element by element (for) 645863535 0:30.929 1,04 Element by element (while) 651068500 0:30.064 1,00 ============ Move part of array right by 1: string ============ Array.Copy() 403684808 0:30.191 1,62 Element by element (for) 162646202 0:30.051 4,00 Element by element (while) 160947492 0:30.945 4,16
两次运行在
smallArraySize = 256
:f:\MyProgramming\TimSort\Benchmarks\bin\Release>Benchmarks.exe ============ Move part of array right by 1: int ============ Array.Copy() 172632756 0:30.128 1,00 Element by element (for) 91403951 0:30.253 1,90 Element by element (while) 65352624 0:29.141 2,56 ============ Move part of array right by 1: string ============ Array.Copy() 153426720 0:28.964 1,08 Element by element (for) 19518483 0:30.353 8,91 Element by element (while) 19399180 0:29.793 8,80 f:\MyProgramming\TimSort\Benchmarks\bin\Release>Benchmarks.exe ============ Move part of array right by 1: int ============ Array.Copy() 184710866 0:30.456 1,00 Element by element (for) 92878947 0:29.959 1,96 Element by element (while) 73588500 0:30.331 2,50 ============ Move part of array right by 1: string ============ Array.Copy() 157998697 0:30.336 1,16 Element by element (for) 19905046 0:29.995 9,14 Element by element (while) 18838572 0:29.382 9,46
Let's say I want to move a part of an array right by 1. I can either use
Array.Copy
or just make a loop copying elements one by one:private static void BuiltInCopy<T>(T[] arg, int start) { int length = arg.Length - start - 1; Array.Copy(arg, start, arg, start + 1, length); } private static void ElementByElement<T>(T[] arg, int start) { for (int i = arg.Length - 1; i > start; i--) { arg[i] = arg[i - 1]; } } private static void ElementByElement2<T>(T[] arg, int start) { int i = arg.Length - 1; while (i > start) arg[i] = arg[--i]; }
(
ElementByElement2
was suggested by Matt Howells.)I tested it using Minibench, and results surprised me quite a lot.
internal class Program { private static int smallArraySize = 32; public static void Main(string[] args) { BenchArrayCopy(); } private static void BenchArrayCopy() { var smallArrayInt = new int[smallArraySize]; for (int i = 0; i < smallArraySize; i++) smallArrayInt[i] = i; var smallArrayString = new string[smallArraySize]; for (int i = 0; i < smallArraySize; i++) smallArrayString[i] = i.ToString(); var smallArrayDateTime = new DateTime[smallArraySize]; for (int i = 0; i < smallArraySize; i++) smallArrayDateTime[i] = DateTime.Now; var moveInt = new TestSuite<int[], int>("Move part of array right by 1: int") .Plus(BuiltInCopy, "Array.Copy()") .Plus(ElementByElement, "Element by element (for)") .Plus(ElementByElement2, "Element by element (while)") .RunTests(smallArrayInt, 0); var moveString = new TestSuite<string[], string>("Move part of array right by 1: string") .Plus(BuiltInCopy, "Array.Copy()") .Plus(ElementByElement, "Element by element (for)") .Plus(ElementByElement2, "Element by element (while)") .RunTests(smallArrayString, "0"); moveInt.Display(ResultColumns.All, moveInt.FindBest()); moveString.Display(ResultColumns.All, moveInt.FindBest()); } private static T ElementByElement<T>(T[] arg) { ElementByElement(arg, 1); return arg[0]; } private static T ElementByElement2<T>(T[] arg) { ElementByElement2(arg, 1); return arg[0]; } private static T BuiltInCopy<T>(T[] arg) { BuiltInCopy(arg, 1); return arg[0]; } private static void BuiltInCopy<T>(T[] arg, int start) { int length = arg.Length - start - 1; Array.Copy(arg, start, arg, start + 1, length); } private static void ElementByElement<T>(T[] arg, int start) { for (int i = arg.Length - 1; i > start; i--) { arg[i] = arg[i - 1]; } } private static void ElementByElement2<T>(T[] arg, int start) { int i = arg.Length - 1; while (i > start) arg[i] = arg[--i]; } }
Note that allocations are not being measured here. All methods just copy array elements. Since I am on 32-bit OS, an
int
and astring
reference take up the same amount of space on stack.This is what I expected to see:
BuiltInCopy
should be the fastest for two reasons: 1) it can do memory copy; 2)List<T>.Insert
usesArray.Copy
. On the other hand, it's non-generic, and it can do a lot of extra work when arrays have different types, so perhaps it didn't take full advantage of 1).ElementByElement
should be equally fast forint
andstring
.BuiltInCopy
should either be equally fast forint
andstring
, or slower forint
(in case it has to do some boxing).However, all of these suppositions were wrong (at least, on my machine with .NET 3.5 SP1)!
BuiltInCopy<int>
is significantly slower thanElementByElement<int>
for 32-element arrays. When size is increased,BuiltInCopy<int>
becomes faster.ElementByElement<string>
is over 4 times slower thanElementByElement<int>
.BuiltInCopy<int>
is faster thanBuiltInCopy<string>
.Can anybody explain these results?
UPDATE: From a CLR Code Generation Team blog post on array bounds check elimination:
Advice 4: when you’re copying medium-to-large arrays, use Array.Copy, rather than explicit copy loops. First, all your range checks will be “hoisted” to a single check outside the loop. If the arrays contain object references, you will also get efficient “hoisting” of two more expenses related to storing into arrays of object types: the per-element “store checks” related to array covariance can often be eliminated by a check on the dynamic types of the arrays, and garbage-collection-related write barriers will be aggregated and become much more efficient. Finally, we will able to use more efficient “memcpy”-style copy loops. (And in the coming multicore world, perhaps even employ parallelism if the arrays are big enough!)
The last column is the score (total duration in ticks/number of iterations, normalized by the best result).
Two runs at
smallArraySize = 32
:f:\MyProgramming\TimSort\Benchmarks\bin\Release>Benchmarks.exe ============ Move part of array right by 1: int ============ Array.Copy() 468791028 0:30.350 1,46 Element by element (for) 637091585 0:29.895 1,06 Element by element (while) 667595468 0:29.549 1,00 ============ Move part of array right by 1: string ============ Array.Copy() 432459039 0:30.929 1,62 Element by element (for) 165344842 0:30.407 4,15 Element by element (while) 150996286 0:28.399 4,25 f:\MyProgramming\TimSort\Benchmarks\bin\Release>Benchmarks.exe ============ Move part of array right by 1: int ============ Array.Copy() 459040445 0:29.262 1,38 Element by element (for) 645863535 0:30.929 1,04 Element by element (while) 651068500 0:30.064 1,00 ============ Move part of array right by 1: string ============ Array.Copy() 403684808 0:30.191 1,62 Element by element (for) 162646202 0:30.051 4,00 Element by element (while) 160947492 0:30.945 4,16
Two runs at
smallArraySize = 256
:f:\MyProgramming\TimSort\Benchmarks\bin\Release>Benchmarks.exe ============ Move part of array right by 1: int ============ Array.Copy() 172632756 0:30.128 1,00 Element by element (for) 91403951 0:30.253 1,90 Element by element (while) 65352624 0:29.141 2,56 ============ Move part of array right by 1: string ============ Array.Copy() 153426720 0:28.964 1,08 Element by element (for) 19518483 0:30.353 8,91 Element by element (while) 19399180 0:29.793 8,80 f:\MyProgramming\TimSort\Benchmarks\bin\Release>Benchmarks.exe ============ Move part of array right by 1: int ============ Array.Copy() 184710866 0:30.456 1,00 Element by element (for) 92878947 0:29.959 1,96 Element by element (while) 73588500 0:30.331 2,50 ============ Move part of array right by 1: string ============ Array.Copy() 157998697 0:30.336 1,16 Element by element (for) 19905046 0:29.995 9,14 Element by element (while) 18838572 0:29.382 9,46
原文:https://stackoverflow.com/questions/1279407
最满意答案
您需要删除第一个片段,您可以通过使用
replace
或首先调用remove
然后add
为了能够按后退按钮将事务添加到后台堆栈,您可以通过在片段管理器上调用
addToBackStack(tag)
来实现。 标签可能为空。You need to remove the first fragment, you can do that either by using
replace
or first callingremove
thenadd
To be able to press the back button add the transaction to the back stack,you do that by calling
addToBackStack(tag)
on your fragment manager. Tag may be null.
相关问答
更多-
查看问题从FirstFragment调用SecondFragment(View Issue Calling SecondFragment from FirstFragment)[2024-02-18]
您需要删除第一个片段,您可以通过使用replace或首先调用remove然后add 为了能够按后退按钮将事务添加到后台堆栈,您可以通过在片段管理器上调用addToBackStack(tag)来实现。 标签可能为空。 You need to remove the first fragment, you can do that either by usingreplace or first calling remove then add To be able to press the back button a ... -
你可以在中途完成。 不要试图从片段中获取活动的权重,而是尝试从片段中设置活动的权重。 public class MainActivity extends FragmentActivity { ... private float weight; public void setWeight(float weight) { this.weight = weight; } public float getWeight() { return this.weight(); } ...
-
似乎我找到了解决我的问题的办法。 这里和这里给出了很好的解释。 这是我的例子: pulic class MyActivity extends FragmentActivity{ private ViewPager pager; private TitlePageIndicator indicator; private TabsAdapter adapter; private Bundle savedInstanceState; @Override public void onCreate(Bundl ...
-
首先,为了在启动时初始加载frstfragment,必须将frstfragments视图定义为主活动视图的默认视图。 其次,在选择bottomnavigation菜单时,您需要有一个framelayout来加载每个片段的视图。在这里,您只是简单地替换mainactivity的父布局,这是一个错误的概念,我相信。 所以你需要创建一个子框架布局来为每个bottomnavigation菜单项加载片段视图。 编辑: 我的布局包含底部导航, ...
-
在onPause和onResume Fragment和Activity由android管理,所以你可能无法检索成员变量,所以在你的Fragment的newInstance方法中克服这个问题使用像 public static Fragment newInstance(int id, String page){ Bundle bundle = new Bundle(); bundle.putString("page", page); bundle.putInt("ID", page); ...
-
将OnTextChangedListener放在声明EditText的片段中。 Put the OnTextChangedListener in the fragment that declares the EditText.
-
整个活动在轮换期间被破坏并重新创建。 因此,如果您在Activity.onCreate中调用setItem(0),那么您将始终在内容框架中获取FirstFragment。 看起来很容易就是检测你是否已经在onCreate中设置了一个片段并且没有加载默认值。 使用onSaveInstanceState和/或将片段标记为保留。 除了初始加载之外,我对保留的片段或片段管理没有多少经验,因此只需使用onSaveInstanceState来跟踪加载哪个片段似乎是合适的。 在您的Activity中,覆盖onSaveIn ...
-
addToBackStack不起作用(addToBackStack doesn't work)[2023-11-13]
片段事务上的addToBackStack()是不够的,我们必须在我们自己按下的Back按钮上处理后备栈的弹出。 @Override public void onBackPressed() { if (getFragmentManager().getBackStackEntryCount() > 0 ){ getFragmentManager().popBackStack(); } else { super.onBackPressed(); } } addToBackStack() on ... -
在调用onDestroy()之后重用Fragment是否安全?(Is it safe to reuse Fragment after onDestroy() has been called?)[2023-11-02]
onDestroy功能是销毁所有使用过的变量和消耗的内存。 所有这些都将被标记为Dummy数据,以使garbage collector能够在需要时将它们从内存中删除。 调用onDestroy后再次调用Fragment将从开始到onCreate再次通过生命周期,所有variables和本地object将再次重新初始化。 安全吗? 是的,这是安全的。 您正在考虑更深入地处理已经由OS处理的Fragment的lifeCycle 。 如果要防止Fragment被破坏,可以将其创建为static对象。 onDestr ... -
试试这个,在加载第二个片段时调用方法.addToBackStack(“returnFragment”)并从第一个事务中删除它。 try this, call method .addToBackStack("returnFragment") while loading second fragment and remove it from first transaction.