Название может показаться немного странным, потому что я понятия не имею, как описать это в одном предложении.Удаление из массива, зеркальное (странное) поведение
Для алгоритмов курса мы должны микро-оптимизировать некоторые вещи, каждый из них выясняет, как работает удаление из массива. Назначение - это удалить что-то из массива и повторно выровнять содержимое, чтобы не было пробелов, я думаю, что это очень похоже на то, как std :: vector :: erase работает с C++.
Поскольку мне нравится идея понимать все на низком уровне, я пошел немного дальше и попытался выполнить мои решения. Это показало некоторые странные результаты.
Во-первых, здесь немного кода, который я использовал:
class Test {
Stopwatch sw;
Obj[] objs;
public Test() {
this.sw = new Stopwatch();
this.objs = new Obj[1000000];
// Fill objs
for (int i = 0; i < objs.Length; i++) {
objs[i] = new Obj(i);
}
}
public void test() {
// Time deletion
sw.Restart();
deleteValue(400000, objs);
sw.Stop();
// Show timings
Console.WriteLine(sw.Elapsed);
}
// Delete function
// value is the to-search-for item in the list of objects
private static void deleteValue(int value, Obj[] list) {
for (int i = 0; i < list.Length; i++) {
if (list[i].Value == value) {
for (int j = i; j < list.Length - 1; j++) {
list[j] = list[j + 1];
//if (list[j + 1] == null) {
// break;
//}
}
list[list.Length - 1] = null;
break;
}
}
}
}
Я бы просто создать этот класс и вызвать метод испытания(). Я делал это в цикле 25 раз.
Мои выводы:
- Первый раунд она занимает гораздо больше времени, чем другие 24, я думаю, что это из-за кэширования, но я не уверен.
- Когда я использую значение, которое находится в начале списка, оно должно перемещать больше элементов в памяти, чем когда я использую значение в конце, хотя по-прежнему это занимает меньше времени.
- Benchtimes отличаются совсем немного.
- Когда я включаю прокомментированный, если производительность повышается (10-20%), даже если значение, которое я ищу, находится почти в конце списка (это означает, что если он отключается много раз, не будучи фактически полезным) ,
Я понятия не имею, почему это происходит, есть ли кто-нибудь, кто может объяснить (некоторые из них)? И, может быть, если кто-то увидит, кто такой профессионал, где я могу найти больше информации, чтобы сделать это наиболее эффективным способом?
Edit после тестирования:
Я сделал некоторые испытания и обнаружили некоторые интересные результаты. Я запускаю тест на массив размером в миллион элементов, заполненный миллионом объектов. Я запускаю это 25 раз и сообщаю о кумулятивном времени в миллисекундах. Я делаю это 10 раз и беру среднее значение этого как окончательное значение.
Когда я запускаю тест с моей функцией описано чуть выше здесь я получаю балл: 362,1
Когда я запускаю его с ответом ДОК я получаю балл: 846,4
Так что мой был быстрее, но затем я начал экспериментировать с пустым пустым массивом, и все стало странно. Для того, чтобы избавиться от неизбежного NullPointerExceptions я добавил дополнительную проверку на если (думая, что это разрушило бы немного больше производительности) как так:
if (fromItem != null && fromItem.Value != value)
list[to++] = fromItem;
Это, казалось, не только работать, но и существенно повысить производительность!Теперь я получаю балл: 247,9
Странная вещь, оценки, кажется низким, чтобы быть правдой, но иногда шип, это множество я взял СРЕДНЕМ от: 94, 26, 966, 36, 632, 95, 47, 35, 109, 439
Поэтому дополнительная оценка улучшает мои характеристики, несмотря на выполнение дополнительной проверки. Как это возможно?
Я сейчас буду читать ваши ссылки, надеюсь, это поможет мне. Выглядит неплохо! Я не могу использовать 'Array.RemoveAt()', потому что цель состоит в том, чтобы написать его из «scratch» (показать, что вы понимаете, как это работает). Причина, по которой у меня есть две петли, состоит в том, что первый находит элемент, а секунды продолжаются с того места, где он найден. Я думал, что использование двух циклов может быть более эффективным, чем распределение переменных, чтобы отслеживать материал. Итак, ваша версия функции более эффективна, чем моя? (если да, почему?) – Gideon
@ Gideon - моя версия может быть более эффективной, чем ваша, потому что вы посещаете элементы после индекса удаления более одного раза: сначала для внутреннего цикла, во-вторых, для внешнего цикла. Это может испортить кеширование, требуя, чтобы список извлекался из основной памяти дважды. Но, возможно, нет, так что дайте ему тест! Выполнение реальных экспериментов с вариациями кода и выяснение реальных результатов, которые вы здесь делаете, - это именно то, как профессионалы исследуют проблемы производительности. – dbc
Внешний цикл не зацикливается снова из-за 'break;' там. Но я проверю это, спасибо за ответ! Так как это лучший ответ (до сих пор?), Я буду отмечать вас. :) – Gideon