I rarely touch threading with my code, but was facing significant pressure to reduce runtime and so have attempted to utilize the parallel.ForEach loop.
The code executes calculations on bids (Items). The items have been loaded into a List<List<Item>> (the outer List being items of all the same ID and the inner list being the various items). I've included the class Item at the end.
List<List<Item>> queryItemsByAcceptID = phyBidList.GroupBy(bids => bids.acceptID)
.Select(group => group.ToList())
.ToList();
All bids have a property (settlement period) that separates them by time e.g. period 1 = 00:00-00:30, period 2 = 00:30-01:00 and so on.
A bid from period 2 doesn't need any information from a bid from period 1.
Hence, I separated the bids into their settlement periods, to run the calculations within a parallel.ForEach.
List<Item> phyBidList = new List<Item>();
var queryMassBySetPeriod = phyBidList.GroupBy(x => x.settlementPeriod)
.Select(group => group.ToList())
.ToList();
However, when I run the code I get inconsistent results both with the 'unParallel code' and with previous outputs when run multiple times. This leads me to think my code isn't 'thread safe', as the 'unParallel code' is both correct and consistent (but just way too slow).
Is this thread safe? And what should I do to generate consistent results?
I'm wondering if I should be locking the daylight out of everything...
acceptIdItem.FPN.Add(fpn);
acceptIdItem.qAboPosArea.Add(tempQABOposArea);
acceptIdItem.qAboNegArea.Add(tempQABOnegArea);
However, I'm not sure if locking is appropriate because the threads aren't (or at least shouldn't be) accessing the same variables... both because information from other blocks isn't required and bids only go through calculation once.
P.S. I've included code below, I've tried to remove what I don't think is necessary to make it shorter and easier to read.
Parallel.ForEach(queryMassBySetPeriod, block =>
{
Console.WriteLine("GroupBy UnitID");
var queryItemsByUnitID = block.GroupBy(bids => bids.unitID)
.Select(group => group.ToList())
.ToList();
Console.WriteLine("GroupBy AcceptID");
queryItemsByAcceptID = block.GroupBy(bids => bids.acceptID)
.Select(group => group.ToList())
.ToList();
Console.WriteLine("Beginning mass interpretation...");
foreach (var list in queryItemsByAcceptID)
{
int bY = 0;
foreach (var acceptIdItem in list)
{
DateTime fromTime = acceptIdItem.fromTime;
DateTime toTime = acceptIdItem.toTime;
TimeSpan duration = toTime - fromTime;
for (int i = 0; i < (duration.Minutes); i++) //qTime fix (duration.Minutes + 1)
{
var queryPNdata = (from item in PNList
where item.unitID == acceptIdItem.unitID && item.fromTime <= fromTime && item.toTime >= fromTime
select item).FirstOrDefault();
int time = (acceptIdItem.qTimes[i] - acceptIdItem.fromTime).Minutes;
float boa = MathHelper.calcBOA(acceptIdItem.fromLevel, acceptIdItem.toLevel, (duration).Minutes, time);
float fpn = MathHelper.calcFPN(queryPNdata.fromLevel, queryPNdata.toLevel, duration.Minutes, time);
acceptIdItem.qTimes.Add(fromTime + i * ((toTime - fromTime) / duration.Minutes));
acceptIdItem.boa.Add(boa);
acceptIdItem.FPN.Add(fpn);
string[] tempBOUR = new string[6]; string[] tempBOLR = new string[6];
float[] tempQABOneg = new float[6]; float[] tempQABOpos = new float[6];
for (int k = 1; k < 7; k++)
{
//calculate tempBOUR/ tempBOLR/ tempQABOpos/ tempQABOneg
}
acceptIdItem.BOUR.Add(tempBOUR);
acceptIdItem.BOLR.Add(tempBOLR);
acceptIdItem.qAboPos.Add(tempQABOpos);
acceptIdItem.qAboNeg.Add(tempQABOneg);
}
int aZ = 0; //declared outside the loop to access later
for (aZ = 0; aZ < (acceptIdItem.qAboNeg.Count() - 1); aZ++)
{
float[] tempQABOnegArea = new float[6]; float[] tempQABOposArea = new float[6];
for (int k = 1; k < 7; k++)
{
//calculate tempQABOnegArea/ tempQABOposArea
}
acceptIdItem.qAboPosArea.Add(tempQABOposArea);
acceptIdItem.qAboNegArea.Add(tempQABOnegArea);
}
bY++;
}
}
});
At the beginning information is assigned and the class is added to a list (phyBidList). Here is the class...
class Item
{
public String unitID, acceptID, prevAcceptID, type;
public DateTime fromTime, toTime, acceptTime;
public int settlementPeriod, duration;
public List<DateTime> qTimes = new List<DateTime>();
public List<string[]> BOLR = new List<string[]>();
public List<string[]> BOUR = new List<string[]>();
public List<float[]> qAboNeg = new List<float[]>();
public List<float[]> qAboPos = new List<float[]>();
public List<float> boa = new List<float>();
public List<float> prevBOA = new List<float>();
public List<float> FPN = new List<float>();
public List<float[]> qAboNegArea = new List<float[]>();
public List<float[]> qAboPosArea = new List<float[]>();
}
****Edit****
In response to #calum-mcveigh, I changed the lists to concurrentBags using the below snippet, and other relevant changes. However, it still produces inconsistent results. I've put the full code on here https://pastebin.com/EgnE2285
ConcurrentBag<ConcurrentBag<Item>> queryMass = new ConcurrentBag<ConcurrentBag<Item>>();
foreach (var items in queryMassBySetPeriod)
{
ConcurrentBag<Item> item = new ConcurrentBag<Item>();
foreach (var bid in items)
{
item.Add(bid);
}
queryMass.Add(item);
}
To ensure thread safety you could use a concurrent collection such as ConcurrentBag<T> rather than a List<T>
You can read more about threadsafe collections here
Variable queryItemsByAcceptID is declared outside Parallel.ForEach but is set and used inside it. Is stopped looking there, but maybe there are other variables with the same problem.
Related
I have a video clip which contains a bunch of Meta data (Mostly single line of strings), which in turn are linked to a specific frame. I managed to separate the speed data from each line of the Meta data and store it with in another list lc4_highest_speed_2
for (int h = 0; h < lc4_file_calculations.Count; h++)
{
string hold_variable = lc4_file_calculations[h].Replace("-", ",");
var mySplitResult2 = hold_variable.Split(',');
var speed = mySplitResult2[mySplitResult2.Length - 45];
lc4_highest_speed.Add(speed + ":" + h);
}
for (int f = 0; f < lc4_highest_speed.Count; f++)
{
string hoe3 = lc4_highest_speed[f];
var mySplitResult3 = hoe3.Split(':');
var speed2 = mySplitResult3[mySplitResult3.Length - 2];
var speed3 = mySplitResult3[mySplitResult3.Length - 1];
string speed_test = speed2.ToString();
lc4_highest_speed_2.Add(speed2 + " - " + speed3);
}
The new list holds data like this 012 - 82 the first part before the - is the speed and the other is the index number related to value from another string. I have tried things like concat, however it hasn’t worked. What would be the best way to get the highest speed the element before the - while also keeping the relation of the index number the number after the -.
Thank you
Try following :
string[] input = {
"012 - 82",
"012 - 83",
"012 - 84",
"012 - 85",
"012 - 86",
"012 - 87",
"13 - 102",
"13 - 103",
"13 - 104",
"13 - 105",
"13 - 106"
};
var output = input
.Select(x => x.Split(new char[] { '-' }))
.Select(x => new { speed = int.Parse(x.First()), index = int.Parse(x.Last()) })
.OrderByDescending(x => x.speed)
.GroupBy(x => x.speed)
.First()
.ToList();
A solution in O(n) time complexity return the higher speed value with its high index value.
int highestSpeed = 0;
int indexNumber = 0;
foreach (var item in lc4_highest_speed_2)
{
var values = item.Split('-');
if (values?.Count() == 2)
{
int.TryParse(values[0], out int parsedValue);
if (parsedValue > highestSpeed)
{
highestSpeed = parsedValue;
int.TryParse(values[1], out indexNumber);
continue;
}
int.TryParse(values[1], out int parsedIndex);
if (highestSpeed == parsedValue && parsedIndex > indexNumber) indexNumber = parsedIndex;
}
}
You can leverage a tuple to do this. Tuples automatically define an ordering based on their members that implement IComparable<T>. (Comparison is done in the order that the tuple's members are declared.)
Since an int implements IComparable<T> if we construct a tuple with the first element being the integer speed and the second the integer index, then the comparison operation generated for the tuple will be exactly what you need.
Thus you can solve this with a Linq expression that splits each row into the speed and index strings, parses those strings into ints and creates a tuple for each pair of speed and index. Then you can find the biggest using IEnumerable.Max():
var max = lc4_highest_speed_2.Select(item =>
{
var elements = item.Split("-");
return (speed: int.Parse(elements[0].Trim()), index: int.Parse(elements[1].Trim()));
}).Max();
Note that max is a tuple with an int first element called speed and an int second element called index.
Try it on .Net Fiddle
You can store all the speed value in the list of integer where you can find the mx value and using its index you then can find the relation between the speed and index value.
For the reference, I have created the small program. Hope you will understand
using System;
using System.Collections.Generic;
using System.Linq;
class HelloWorld {
static void Main() {
List<string> data = new List<string>();
List<int> intdata = new List<int>();
data.Add("012 - 82");
data.Add("013 - 102");
for(int i=0;i<data.Count;i++){
intdata.Add(Int16.Parse(data[i].Substring(0,data[i].IndexOf("-"))));
}
Console.WriteLine(intdata.Max());
}
}
Another way to do it:
var maxSpeed = lc4.Max(x => Convert.ToInt32(x.Split(' ')[0]));
var maxLine = lc4.FirstOrDefault(x => x.StartsWith($"{maxSpeed} "));
var maxIndex = Convert.ToInt32(maxLine.Split('-')[1].Substring(1));
or if you want all indices:
var maxSpeed = lc4.Max(x => Convert.ToInt32(x.Split(' ')[0]));
var maxLines = lc4.Where(x => x.StartsWith($"{maxSpeed} "));
var maxIndexes = maxLines.Select(x => Convert.ToInt32(x.Split('-')[1].Substring(1)));
Question: How to distribute items in number of lists? following are examples:
Imagine I have a 8 lists, and 5 items. in this case list 1 to 5 will have 1 item. rest of lists remain empty.
Now If I have 8 lists and 16 items each list will have 2 items.
If I have 8 lists and 11 items, list 1 to 3 will have two items. rest of the lists will have 1 item.
var items = new List<object>();
var containers = new List<List<object>>();
int c = -1; // indexer for container.
for(int i = 0; i < items.Count; i++)
{
// disturbute items to containers
if (i % (items.Count/containers.Count) == 0) c++; // this is wrong. when to increment?
containers[c].Add(items[i]);
}
I'm pretty sure only if statement is wrong. Its getting confusing how to handle i.
Try this:
static void Main(string[] args)
{
var items = new List<object>() {1, 2, 3, 4, 5, 6};
var containers = new List<List<object>>() { new List<object>(), new List<object>(), new List<object>()};
int c = 0; // indexer for container.
int containerCount = containers.Count;
for (int i = 0; i < items.Count; i++, c++)
{
c = c % containerCount;
containers[c].Add(items[i]);
}
}
Think about the algorithm. When inserting item, you insert it to a list and then move to next one. When reaching last list, start inserting back to the first. Code (written in notepad, so may not compile):
var items = new List<object>();
var containers = new List<List<object>>();
int c = 0;
for (int i = 0; i < items.Count; i++)
{
c++; // move to next container
// when reached to the end, insert again to first list
if (c == containers.Count)
{
c = 0;
}
containers[c].Add(items[i]);
}
This can be made a bit shorter:
var items = new List<object>();
var containers = new List<List<object>>();
int c = 0;
foreach (int item in items)
{
c = (c == containers.Count - 1) ? 0 : c + 1;
containers[c].Add(item);
}
If you want to isolate this behavior into something reusable and testable:
public class ListBalancer
{
public void BalanceItemsBetweenLists<T>(
IEnumerable<T> input,
IEnumerable<IList<T>> targets)
{
var inputArray = input as T[] ?? input.ToArray();
var targetArray = targets as IList<T>[] ?? targets.ToArray();
var currentTargetIndex = 0;
foreach (var item in inputArray)
{
targetArray[currentTargetIndex].Add(item);
currentTargetIndex++;
if (currentTargetIndex == targetArray.Length) currentTargetIndex = 0;
}
}
}
[TestClass]
public class ListBalancerTests
{
[TestMethod]
public void BalancesListsWhenAddingItems()
{
var source = Enumerable.Range(1, 11);
var targets = Enumerable.Range(1, 8).Select(n => new List<int>()).ToArray();
new ListBalancer().BalanceItemsBetweenLists(source, targets);
Assert.AreEqual(2, targets[0].Count);
Assert.AreEqual(2, targets[1].Count);
Assert.AreEqual(2, targets[2].Count);
Assert.AreEqual(1, targets[3].Count);
}
}
It seems like a little bit of extra work. But you probably found that the process of debugging when it didn't do what was expected took a little extra time, too. It might have been necessary to start a console application or some other app to test the behavior. If you write a class with a unit test you might still have to debug, but it's faster and more self-contained. You finish the one class with the one behavior, test it, and then move on.
Personally I found that once I formed the habit I could write code a little faster and with fewer bugs because I made my debugging process smaller and easier.
I'm making a basic Deal or No Deal game, in doing so I have to pick 10 finalists from an array, at random, without repeats.
I have my structure and arrays set out like this
public struct People
{
public string firstname;
public string lastname;
public int age;
}
class Program
{
public static People[] People1 = new People[40];
public static People[] Finalists1 = new People[10];
public static People[] Finalist1 = new People[1];
And my finalists method set out like this
Random rand = new Random();
for (int i = 0; i < Finalists1.Length; i++)
{
num = rand.Next(0, People1.Length);
Finalists1[i].lastname = People1[num].lastname;
Finalists1[i].firstname = People1[num].firstname;
Finalists1[i].age = People1[num].age;
}
How can I eliminate duplicate entries, while maintaining 10 people in the array?
Since initial array doesn't contain duplicates, you can sort it in random order and pick up 10 top items:
Finalists1 = People1
.OrderByDescending(item => 1) // if people have some points, bonuses etc.
.ThenBy(item => Guid.NewGuid()) // shuffle among peers
.Take(10) // Take top 10
.ToArray(); // materialize as an array
If people are selected to the final are not completely random (e.g. contestant can earn points, bonuses etc.) change .OrderByDescending(item => 1), e.g.
.OrderByDescending(item => item.Bonuses)
If you don't want to use Linq, you can just draw Peoples from urn without returning:
private static Random random = new Random();
...
List<People> urn = new List<People>(People1);
for (int i = 0; i < Finalists1.Length; ++i) {
int index = random.Next(0, urn.Count);
Finalists1[i] = urn[index];
urn.RemoveAt(index);
}
You can hold a list or hash set of numbers you have already drawn. Then just roll the dice again to get another random number.
Random rand = new Random();
HashSet<int> drawnNumbers = new HashSet<int>();
for (int i = 0; i < Finalists1.Length; i++)
{
do
{
num = rand.Next(0, People1.Length);
}
while (drawnNumbers.Contains(num));
Finalists1[i] = People1[num];
}
You can change the type of Finalists1 to a HashSet, that does not allow duplicates.
Then change your loop to
while(Finalists1.Length < 10)
{
// random pick from array People1 (you don't need to create a new one)
num = rand.Next(0, People1.Length);
var toAdd = People1[num];
// add to hash-set. Object won't be added, if already existing in the set
Finalists1.Add(toAdd);
}
You probably need to override the Equals method of class People, if you really need to create a new object to add to the hash-set.
You can group people array and select distinct that way.
If you use List you can remove person from the list
`var peopleArray = new People[40];
var peopleCollection = peopleArray.GroupBy(p => new { p.age, p.firstname, p.lastname }).Select(grp => grp.FirstOrDefault()).ToList();
var finalists = new People[10];
var rand = new Random();
for (var i = 0; i < finalists.Length; i++)
{
var index = rand.Next(0, peopleCollection.Count);
var person = peopleCollection[index];
finalists[i].lastname = person.lastname;
finalists[i].firstname = person.firstname;
finalists[i].age = person.age;
peopleCollection.Remove(person);
}
shuffle and take the first 10, for example
People1.Shuffle();
Finalists1= People1.Take(10).ToArray();
you can find shuffle code from StackOverflow or search for "Fisher-Yates shuffle C#" Below methods are taken from This SO Post. Read the answers for more information on why GUID is not used etc..
public static class ThreadSafeRandom
{
[ThreadStatic] private static Random Local;
public static Random ThisThreadsRandom
{
get { return Local ?? (Local = new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); }
}
}
static class MyExtensions
{
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
Swap each selected element in People1 to with the end of the array, and decrement an end-of-array index so that you're only selecting from what's left on the next iteration.
People tempPerson = new People;
int lastElem = People1.length - 1;
for (int i = 0; i < Finalists1.Length; i++)
{
num = rand.Next(0, lastElem + 1);
Finalists1[i] = People1[num];
//swap last entry in People1 with People1[num]
tempPerson = People1[num];
People1[num] = People1[lastElem];
People1[lastElem] = tempPerson;
lastElem--;
}
Sorry if there's a syntax error, I'm mostly using Java and C# these days.
BTW You don't have to set the fields individually since each array stores objects of type Person.
I build a list:
int TOTAL = 10;
List<MyObject> myList = myOtherList.Select
(s => new MyObject
{
Prop1 = s.prop1
})
.ToList<MyObject>();
Here myList have M elements (M = myList.Count())
I need to append N elements to myList so that the myList have TOTAL elements, in other words N+M = TOTAL.
How can I accomplish it?
Just keep adding new items until you hit your desired size:
while (myList.Count < TOTAL)
{
myList.Add(NewItem);
}
LINQ approach:
myList.AddRange(Enumerable.Range(0, TOTAL - myList.Count).Select(i => myList2[i]));
just remember to check whether the myList.Count is smaller than TOTAL, and myList2 has enough elements.
This will get "n" elements from myOtherList and add them to myList.
myList.AddRange(myOtherList.Select(s => new MyObject { Prop1 = s.prop1 }).Take(n));
The nice thing about Take is that if there are not enough elements in myOtherList, it won't cause an error - it'll just get all the elements.
If count exceeds the number of elements in source, all elements of source are returned.
If count is less than or equal to zero, source is not enumerated and an empty IEnumerable is returned.
Here a full example illustratting how you can achieve this with a single line of code
list.AddRange(list2.Take(total - list.Count));
full example
internal class Program
{
private static void Main(string[] args)
{
List<MyObject> list = new List<MyObject>();
for (int i = 0; i < 5; i++)
{
list.Add(new MyObject() {Id = i}
);
}
List<MyObject> list2 = new List<MyObject>();
for (int i = 0; i < 5; i++)
{
list2.Add(new MyObject() {Id = i});
}
int total = 10;
list.AddRange(list2.Take(total - list.Count));
}
class MyObject
{
public int Id { get; set; }
}
}
You can use myList.AddRange() with a limited list in parameter or use myList.Add() in the ForEach Linq.
int TOTAL = 10;
List<MyObject> myList = something;
if (myList < TOTAL)
{
myOtherList.Select
(s => new MyObject
{
Prop1 = s.prop1
})
.ToList<MyObject>()
.Take(TOTAL - myList.Count)
.ForEach(ob => myList.Add(ob));
}
or
int TOTAL = 10;
List<MyObject> myList = something;
if (myList < TOTAL)
{
myList.AddRange(myOtherList.Select
(s => new MyObject
{
Prop1 = s.prop1
})
.ToList<MyObject>()
.Take(TOTAL - myList.Count));
}
What about:
public static IEnumerable<T> ConcatN<T>(this IEnumerable<T> source, IEnumerable<T> source2, int maxSize)
{
if (maxSize == 0)
throw new ArgumentException(
"maxSize cannot be zero", "maxSize");
var sourceCount = source.Count();
if (sourceCount > maxSize)
throw new ArgumentException(
"source has more elements than the given maxSize");
if (sourceCount + source2.Count() < maxSize)
throw new ArgumentOutOfRangeException(
"source and source2 has combined, more elements than the given maxSize");
return source.Concat(source2.Take(maxSize - sourceCount));
}
If the number of items in source exceeds the given maxSize, an exception is throwen.
If the number of items in source + source2 is less than the given maxSize, an exception is throwen.
Usage:
var number1 = Enumerable.Range(0, 10);
var number2 = Enumerable.Range(100, 10);
var number12 = number1.ConcatN(number2, 15);
Console.WriteLine("Length = {0} | Sum = {1}",
number12.Count(), number12.Sum());
// Output: "Length = 15 | Sum = 555"
Try
myList.AddRange()
this is used to add a list to your existing list. (It'll work with any IEnumerable of the same type as the existing list).
Marked down... ooooh I didn't realise we had to write entire solutions for people and there was me thinking that I was wise spending 10 years learning software development when I could have just used stack overflow instead of my brain
For some reason it seems like my LinkedList is outperforming my List. I have a LinkedList because there is part of the code where I have to rearrange the children a lot. Everything after that bit of rearranging is simply looping over the data and performing calculations. I was previously just iterating over the LinkedList using an Iterator, but then I thought that I should simultaneously store a List of the same elements so I can iterate over them faster. Somehow, adding the List and iterating over that was significantly slower for the exact same thing. Not sure how this could be, so any info is helpful.
Here is roughly the code that I would EXPECT to be faster:
class Program {
public static void Main(string[] args) {
var originalList = new List<Thing>();
for (var i = 0; i < 1000; i++) {
var t = new Thing();
t.x = 0d;
t.y = 0d;
originalList.Add(t);
}
var something = new Something(originalList);
for (var i = 0; i < 1000; i++) {
var start = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
something.iterate();
time += (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - start;
Console.Out.WriteLine(time / (i + 1));
}
}
class Thing {
public double x {get; set;}
public double y {get; set;}
}
class Something {
private List<Thing> things;
private LinkedList<Thing> things1 = new LinkedList<Thing>();
private List<Thing> things2 = new List<Thing>();
public Class(List<Thing> things) {
this.things = things;
for (var i = 0; i < things.Count; i++) {
things1.AddLast(things[i]);
things2.Add(things[i]);
}
}
public void iterate() {
//loops like this happen a few times, but the list is never changed, only the
//objects properties in the list
for (var i = 0; i < things2.Count; i++) {
var thing = things2[i];
thing.x += someDouble;
thing.y += someOtherDouble;
}
}
}
}
This was what I was doing first, which I think SHOULD be SLOWER:
class Program {
public static void Main(string[] args) {
var originalList = new List<Thing>();
for (var i = 0; i < 1000; i++) {
var t = new Thing();
t.x = 0d;
t.y = 0d;
originalList.Add(t);
}
var something = new Something(originalList);
for (var i = 0; i < 1000; i++) {
var start = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
something.iterate();
time += (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - start;
Console.Out.WriteLine(time / (i + 1));
}
}
class Thing {
public double x {get; set;}
public double y {get; set;}
}
class Something {
private List<Thing> things;
private LinkedList<Thing> things1 = new LinkedList<Thing>();
public Class(List<Thing> things) {
this.things = things;
for (var i = 0; i < things.Count; i++) {
things1.AddLast(things[i]);
}
}
public void iterate() {
//loops like this happen a few times, but the list is never changed, only the
//objects properties in the list
var iterator = things1.First;
while (iterator != null) {
var value = iterator.Value;
value.x += someDouble;
value.y += someOtherDouble;
iterator = iterator.Next;
}
}
}
}
It's difficult to verify anything since it doesn't compile, so I can't run it on my machine, but still there are some big problems:
You should not be using DateTime.Now to measure performance. Instead use Stopwatch, which is capable of high fidelity time measurements, e.g.:
var stopwatch = Stopwatch.StartNew();
//do stuff here
stopwatch.Stop();
double timeInSeconds = stopwatch.Elapsed.TotalSeconds;
Your arithmetic is fundametally flawed in the following line:
DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond
Ticks are represented as integral numbers (long here) and the result of division is not a real number, but is truncated. E.g. 55 / 7 = 7. Therefore it definitely not a stable way of benchmarking.
Additionally, run the benchmark with more elements and ensure you do it in the Release mode.
Based on your code, the cause of slow performance is not the iteration, it is from the action of adding element to the List<T>.
This post does a very good job on comparing the List and the LinkedList. List<T> is based on array, it is allocated in one contiguous block, so when you add more elements to it, the array will be resized, which cause the slow performance in your code.
If you come from the C++ world, List<T> in C# is equivalent to vector<T> in STD, while LinkedList<T> is equivalent to list<T> in STD.