Здравствуйте, vf, Вы писали:
vf>Смотри показатели для ReaderWriterLock для 11 потоков значительно улучшаются. 51 поток это неэфективно для двухядерного процессора, с гипертриденгом, ИМХО, как раз 4-6 ну может 8 потоков и должно работать для максимальной отдачи.
Уже третий день угораю над топиком Даже Sinclair повёлся — жуть
Какой ещё 51 поток ??? Там 51 задача во ThreadPool'е. Автор-то дёргает BeginInvoke() у делегата А так как ядер всего 2, и блокируемая операция занимает меньше кванта, то реальной одновременности практически нет. Цифирь итоговая от запуска к запуску может влёгкую на порядок различаться.
M>>кроме того TestReaderWriterMultiTime и TestFastResourceLockMultiTime кушают 100% процессора на 50 threads, lock — около нуля
vf>Ну понятно, что лок запихнул все потоки в очередь, и достает по одному.
В том и дело, что нет там никакой очереди. lock успевает отработать за квант, а ядер всего два, и ThreadPool не стартует worker'ов больше чем ядер. В итоге блокировок практически не бывает. Как там автор нулевую загрузку увидел — не понимаю. Она — после "разогрева" ThreadPool'а — тоже должна быть около 100%. Разве что у автора нетбук
S>>А можно для тупых объяснить, каким образом вы при помощи lock разрешите одновременный доступ на чтение? S>>Весь смысл ReaderWriterLock в том, чтобы 1000 читателей не выстраивались в очередь, а читали одновременно. А>тупой может написать кусок кода (примеры экзотического программинга, типа распечатки на принтер энциклопедии из залоченной секции, не берём, реалистичный пример — запись/чтение в Dictionary в залоченной секции), который показывает, что ReaderWriterLockSlim выгоднее lock и закопипастить его сюда?
D>Какой ещё 51 поток ??? Там 51 задача во ThreadPool'е. Автор-то дёргает BeginInvoke() у делегата А так как ядер всего 2, и блокируемая операция занимает меньше кванта, то реальной одновременности практически нет. Цифирь итоговая от запуска к запуску может влёгкую на порядок различаться.
D>В том и дело, что нет там никакой очереди. lock успевает отработать за квант, а ядер всего два, и ThreadPool не стартует worker'ов больше чем ядер. В итоге блокировок практически не бывает. Как там автор нулевую загрузку увидел — не понимаю. Она — после "разогрева" ThreadPool'а — тоже должна быть около 100%. Разве что у автора нетбук
завтра посмотрю, что будет, если через ThreadPool, с выставлением MinThreads
не могу никак придумать пример, на котором был бы выигрыш от ReaderWriterLockSlim по сравнению с lock более существенный, чем в 2 раза при чтении (часто при существенном при проигрыше в записи), это нормально ? больше не получится?
при том оверхед от ReaderWriterLockSlim в 2 раза больше
примеры экзотического программинга, типа распечатки на принтер энциклопедии из залоченной секции, не берём, реалистичный пример — запись/чтение в Dictionary в залоченной секции
пример:
Tests.exe 5000 1 50 > console.log
TestLockMultiTime: timer 5000ms, readersCount 50, writersCount 1, reads 6308033, writes 189967, readLock 792.640114596737ns, writeLock 26320.3609047887ns
TestReaderWriterMultiTime: timer 5000ms, readersCount 50, writersCount 1, reads 11098834, writes 24919, readLock 450.497773009309ns, writeLock 200650.106344556ns
1 thread пишет, 50 threads читают, на 1 запись - 1000 чтений: за фиксированное время при ReaderWriterLockSlim делается в 2 раза больше чтений, но в 10 раз меньше записей ...
2х ядерный процессор, .net 3.5
public class LockTestProgram
{
[DllImport("kernel32.dll")]
static extern bool SwitchToThread();
static IDictionary<long, long> dictionary = new Dictionary<long, long>();
public static void Main(string[] args)
{
if (args.Length != 3)
{
System.Console.WriteLine("usage: progName time(millis) writersCount readersCount");
return;
}
for (int i = 0; i < 1000000; i++ )
{
dictionary[i] = i;
}
int millis = int.Parse(args[0]);
int writersCount = int.Parse(args[1]);
int readersCount = int.Parse(args[2]);
TestLockMultiTime(millis, writersCount, readersCount);
Thread.Sleep(200);
TestLockMultiTime(millis, writersCount, readersCount);
Thread.Sleep(200);
TestReaderWriterMultiTime(millis, writersCount, readersCount);
Thread.Sleep(200);
TestReaderWriterMultiTime(millis, writersCount, readersCount);
Thread.Sleep(200);
// multithreaded with fixed number of interations
// if (args.Length != 3)
// {
// System.Console.WriteLine("usage: progName load writersCount readersCount");
// return;
// }
//
// int load = int.Parse(args[0]);
// int writersCount = int.Parse(args[1]);
// int readersCount = int.Parse(args[2]);
//
// TestLockMulti(load, writersCount, readersCount);
// TestLockMulti(load, writersCount, readersCount);
// TestReaderWriterMulti(load, writersCount, readersCount);
// TestReaderWriterMulti(load, writersCount, readersCount);
// monothreaded
// TestEmptyLoop(load);
// TestEmptyLoop(load);
// TestLock(load);
// TestLock(load);
// TestReaderWriterLockSlim_Read(load);
// TestReaderWriterLockSlim_Read(load);
// TestReaderWriterLockSlim_Write(load);
// TestReaderWriterLockSlim_Write(load);
}
#region multithreaded during the fixed time
private static void TestLockMultiTime(int millis, int writersCount, int readersCount)
{
long reads = 0;
long writes = 0;
object obj = new object();
bool flag = true;
long b;
ThreadStart writer = delegate()
{
while (flag)
{
lock (obj)
{
long localW = Interlocked.Increment(ref writes);
localW %= dictionary.Count;
dictionary[localW] = localW;
}
MakeTemporization(false);
}
};
ThreadStart reader = delegate()
{
while (flag)
{
lock (obj)
{
long localW = Interlocked.Increment(ref reads);
localW %= dictionary.Count;
b = dictionary[localW];
}
MakeTemporization(true);
}
};
Stopwatch start = Stopwatch.StartNew();
// start threads
IAsyncResult[] writerResults = StartThreads(writersCount, writer);
IAsyncResult[] readerResults = StartThreads(readersCount, reader);
Thread.Sleep(millis);
flag = false;
// wait for the execution
WaitThreads(writer, writerResults);
WaitThreads(reader, readerResults);
long elapsed = start.ElapsedMilliseconds;
System.Console.WriteLine(
string.Format(
"TestLockMultiTime: timer {0}ms, readersCount {1}, writersCount {2}, reads {3}, writes {4}, readLock {5}ns, writeLock {6}ns",
millis, readersCount, writersCount, reads, writes, ((double)millis) / reads * 1000000, ((double)millis) / writes * 1000000));
}
private static void TestReaderWriterMultiTime(int millis, int writersCount, int readersCount)
{
long reads = 0;
long writes = 0;
ReaderWriterLockSlim LOCK = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
bool flag = true;
long b;
ThreadStart writer = delegate()
{
while (flag)
{
LOCK.EnterWriteLock();
try
{
long localW = Interlocked.Increment(ref writes);
localW %= dictionary.Count;
dictionary[localW] = localW;
}
finally
{
LOCK.ExitWriteLock();
}
MakeTemporization(false);
}
};
ThreadStart reader = delegate()
{
while (flag)
{
LOCK.EnterReadLock();
try
{
long localW = Interlocked.Increment(ref reads);
localW %= dictionary.Count;
b = dictionary[localW];
}
finally
{
LOCK.ExitReadLock();
}
MakeTemporization(true);
}
};
Stopwatch start = Stopwatch.StartNew();
// start threads
IAsyncResult[] writerResults = StartThreads(writersCount, writer);
IAsyncResult[] readerResults = StartThreads(readersCount, reader);
Thread.Sleep(millis);
flag = false;
// wait for the execution
WaitThreads(writer, writerResults);
WaitThreads(reader, readerResults);
long elapsed = start.ElapsedMilliseconds;
System.Console.WriteLine(
string.Format(
"TestReaderWriterMultiTime: timer {0}ms, readersCount {1}, writersCount {2}, reads {3}, writes {4}, readLock {5}ns, writeLock {6}ns",
millis, readersCount, writersCount, reads, writes, ((double)millis) / reads * 1000000, ((double)millis) / writes * 1000000));
}
private static int sleepCounter;
private static void MakeTemporization(bool read)
{
// 1- implementation 1: write to console
// System.Console.WriteLine(" ");
// 2- implementation 2: sleepint step = read ? 2000 : 20;
int localSleepCounter = Interlocked.Increment(ref sleepCounter);
if (localSleepCounter % step == 0)
{
Thread.Sleep(1);
}
else
{
Thread.Sleep(0);
//SwitchToThread();
}
// 3- implementation: switch the thread
// SwitchToThread();
}
#endregion
private static IAsyncResult[] StartThreads(int count, ThreadStart threadStart)
{
IAsyncResult[] results = new IAsyncResult[count];
for (int i = 0; i < count; i++)
{
results[i] = threadStart.BeginInvoke(delegate(IAsyncResult ar) { }, null);
}
return results;
}
private static void WaitThreads(ThreadStart threadStart, IAsyncResult[] results)
{
for (int i = 0; i < results.Length; i++)
{
threadStart.EndInvoke(results[i]);
}
}
}
ИМХО замеры ошибочны, сильно не вникал но вот такие вопросы/предположения:
1. Не понятно почему время записи с локом намного больше времени чтения?! Кривой способ измерения...
2. По записи, проигрыш в разы связан со способом измерений, ведь чтобы readwritelockslim получил экслюзивный доступ он наверное дожидается пока читатели закончат читать. Думаю правильнее мерять скорость лока без читателей.
3. По чтению, выйгрыш сильно зависит от времени нахождения в залоченом участке, dictonary слишком быстрый получился, HashCode быстро вычисляется из int и это нивелирует преимущество.
Вообще к производительности readwritelockslim у многих вопросы, вот хорошая статья, там более корректные замеры присутствуют, и решение предлогается.
vf>1. Не понятно почему время записи с локом намного больше времени чтения?! Кривой способ измерения...
время в этих замерах несущественно (откопипастено из других тестов), главное — количество чтений/записи
vf>2. По записи, проигрыш в разы связан со способом измерений, ведь чтобы readwritelockslim получил экслюзивный доступ он наверное дожидается пока читатели закончат читать. Думаю правильнее мерять скорость лока без читателей.
1. не имеет смысла лочить, если есть только ридеры
2. readwritelockslim — для случаев когра запись делается раз в день ?
vf>3. По чтению, выйгрыш сильно зависит от времени нахождения в залоченом участке, dictonary слишком быстрый получился, HashCode быстро вычисляется из int и это нивелирует преимущество.
ну это как руки мыть перед едой — не запихивать в залоченные секции кучу кода
vf>Вообще к производительности readwritelockslim у многих вопросы, вот хорошая статья, там более корректные замеры присутствуют, и решение предлогается.
сейчас посмотрим
vf>2. По записи, проигрыш в разы связан со способом измерений, ведь чтобы readwritelockslim получил экслюзивный доступ он наверное дожидается пока читатели закончат читать. Думаю правильнее мерять скорость лока без читателей.
неправильно прочитал
какой смысл мерить, если есть только writers? в этом случае не имеет смысла использовать readwritelockslim?
Здравствуйте, mihhon, Вы писали:
vf>>2. По записи, проигрыш в разы связан со способом измерений, ведь чтобы readwritelockslim получил экслюзивный доступ он наверное дожидается пока читатели закончат читать. Думаю правильнее мерять скорость лока без читателей.
M>1. не имеет смысла лочить, если есть только ридеры
Я этого не предлогал. Я предложил сравнить время получения экслюзивного доступа когда нет читателей, со временем лока.
M>2. readwritelockslim — для случаев когра запись делается раз в день ?
Я ни разу не майкрософт Но думаю не все так однозначно... на мой взгляд тесты не корретны.
vf>>3. По чтению, выйгрыш сильно зависит от времени нахождения в залоченом участке, dictonary слишком быстрый получился, HashCode быстро вычисляется из int и это нивелирует преимущество. M>ну это как руки мыть перед едой — не запихивать в залоченные секции кучу кода
Понятно, только я не об этом, ладно скажу по другому, конкретно в этом синтетическом тесте реализован вариант в котором readwritelockslim будет показывать худший результат, чем в среднем в реальных приложениях.
Здравствуйте, mihhon, Вы писали:
vf>>2. По записи, проигрыш в разы связан со способом измерений, ведь чтобы readwritelockslim получил экслюзивный доступ он наверное дожидается пока читатели закончат читать. Думаю правильнее мерять скорость лока без читателей. M>неправильно прочитал M>какой смысл мерить, если есть только writers? в этом случае не имеет смысла использовать readwritelockslim?
Я не знаю как он реализован, но писателю как минимум придеться ждать пока закончат читатели, которые начали читать до записи. А может даже хуже...
А если замерить сколько времени нужно на получения эксклюзивного доступа без "помех", тогда можно сказать насколько он медленее обычного лока — ведь и то и то эксклюзивный доступ. Но у первого дополнительные накладные расходы на сложность. А так не понятно что ты замерил, ведь суммарное число операции у readwritelockslim значительно больше, можно сказать что он победил просто у ридеров "приоритет" (условно, из реализации алгоритма и условий тестов) выше.
vf>Я этого не предлогал. Я предложил сравнить время получения экслюзивного доступа когда нет читателей, со временем лока.
readwritelockslim в 2 раза медленнее примерно, и в моно-thread (если просто оверхэд от добавления в код мерить) и в мульти-thread (1 thread пишет, другой читает, время нахождения в залоченом участке 0 — не очень хорошо, конечно ...)
M>>2. readwritelockslim — для случаев когра запись делается раз в день ?
vf>Я ни разу не майкрософт Но думаю не все так однозначно... на мой взгляд тесты не корретны.
одна запись на десять/сто/тысячу чтений, при одном thread, который пишет на 5/10/50/100 читающих: вполне хороший разброс для тестов, во всех трёх случах нет выигрыша (чуть лучше чтение, но запись хуже существенно)
vf>>>3. По чтению, выйгрыш сильно зависит от времени нахождения в залоченом участке, dictonary слишком быстрый получился, HashCode быстро вычисляется из int и это нивелирует преимущество. M>>ну это как руки мыть перед едой — не запихивать в залоченные секции кучу кода
vf>Понятно, только я не об этом, ладно скажу по другому, конкретно в этом синтетическом тесте реализован вариант в котором readwritelockslim будет показывать худший результат, чем в среднем в реальных приложениях.
сделал тот же тест, но ключ в dictionary — string, то же самое соотношение результатов
vf>Я не знаю как он реализован, но писателю как минимум придеться ждать пока закончат читатели, которые начали читать до записи. А может даже хуже...
vf>А если замерить сколько времени нужно на получения эксклюзивного доступа без "помех"
в 2 раза хуже
vf>А так не понятно что ты замерил, ведь суммарное число операции у readwritelockslim значительно больше, можно сказать что он победил просто у ридеров "приоритет" (условно, из реализации алгоритма и условий тестов) выше.
я сравнил lock и ReaderWriterLockSlim, стало инересно, имеет ли смысл ReaderWriterLockSlim использовать, в сценарии, который приближен к программке, которую пишу: 1 thread пишет, 4-8 читают, на 1 запись приходится 10-40 чтений. и посмотрел результаты тестов с разным набором параметров
и никакой выгоды от ReaderWriterLockSlim нет в таком сценарии. а код менее читабельный и можно перепутать/накопипастить EnterWriteLock->ExitReadLock (было такое), (про конструкцию с using написать оболочку вокруг ReaderWriterLockSlim знаю)
vf>Вообще к производительности readwritelockslim у многих вопросы, вот хорошая статья, там более корректные замеры присутствуют, и решение предлогается.
основной вывод из статьи: пишите грамотный код — минимизируйте залоченные секции, lock — достаточно и наиболее быстро
Здравствуйте, mihhon, Вы писали:
vf>>А так не понятно что ты замерил, ведь суммарное число операции у readwritelockslim значительно больше, можно сказать что он победил просто у ридеров "приоритет" (условно, из реализации алгоритма и условий тестов) выше. M>я сравнил lock и ReaderWriterLockSlim, стало инересно, имеет ли смысл ReaderWriterLockSlim использовать, в сценарии, который приближен к программке, которую пишу: 1 thread пишет, 4-8 читают, на 1 запись приходится 10-40 чтений. и посмотрел результаты тестов с разным набором параметров
А тестовое приложение какие результаты показывает для 4-8 читателей?
Попробуй туда FastResourceLock из статьи подставить, любопытно.
M>про конструкцию с using написать оболочку вокруг ReaderWriterLockSlim знаю
Не сталкивался здесь она? Если да, то создавать каждый раз объект — затратно.
vf>А тестовое приложение какие результаты показывает для 4-8 читателей? vf>Попробуй туда FastResourceLock из статьи подставить, любопытно.
TestLockMultiTime: timer 5000ms, readersCount 50, writersCount 1, reads 6630345, writes 59657, readLock 754.108572027549ns, writeLock 83812.4612367367ns
TestReaderWriterMultiTime: timer 5000ms, readersCount 50, writersCount 1, reads 8298059, writes 7583, readLock 602.550548266769ns, writeLock 659369.642621654ns
TestFastResourceLockMultiTime: timer 5000ms, readersCount 50, writersCount 1, reads 8927731, writes 14211, readLock 560.052716642112ns, writeLock 351840.123847724ns
TestReaderWriterMultiTime, TestFastResourceLockMultiTime - 100% of CPU !!!! with 50 readers
TestLockMultiTime: timer 5000ms, readersCount 10, writersCount 1, reads 5959031, writes 80969, readLock 839.062592559092ns, writeLock 61752.028554138ns
TestReaderWriterMultiTime: timer 5000ms, readersCount 10, writersCount 1, reads 6192030, writes 35970, readLock 807.489627795731ns, writeLock 139004.726160689ns
TestFastResourceLockMultiTime: timer 5000ms, readersCount 10, writersCount 1, reads 6318584, writes 77415, readLock 791.316535476936ns, writeLock 64586.9663501905ns
FastResourceLock делает в 2 раза больше записей чем ReaderWriterLockSlim, оба делают чуть больше чтений но проигрывают сильно в количестве записей lock-у
кроме того TestReaderWriterMultiTime и TestFastResourceLockMultiTime кушают 100% процессора на 50 threads, lock - около нуля
M>>про конструкцию с using написать оболочку вокруг ReaderWriterLockSlim знаю
vf>Не сталкивался здесь она? Если да, то создавать каждый раз объект — затратно.
da
source
public class LockTestProgram
{
[DllImport("kernel32.dll")]
static extern bool SwitchToThread();
static IDictionary<string, string> dictionary = new Dictionary<string, string>();
public static void Main(string[] args)
{
if (args.Length != 3)
{
System.Console.WriteLine("usage: progName time(millis) writersCount readersCount");
return;
}
for (int i = 0; i < 1000000; i++ )
{
dictionary[i.ToString()] = i.ToString();
}
int millis = int.Parse(args[0]);
int writersCount = int.Parse(args[1]);
int readersCount = int.Parse(args[2]);
TestLockMultiTime(millis, writersCount, readersCount);
Thread.Sleep(200);
TestLockMultiTime(millis, writersCount, readersCount);
Thread.Sleep(200);
TestReaderWriterMultiTime(millis, writersCount, readersCount);
Thread.Sleep(200);
TestReaderWriterMultiTime(millis, writersCount, readersCount);
Thread.Sleep(200);
TestFastResourceLockMultiTime(millis, writersCount, readersCount);
Thread.Sleep(200);
TestFastResourceLockMultiTime(millis, writersCount, readersCount);
// multithreaded with fixed number of interations
// if (args.Length != 3)
// {
// System.Console.WriteLine("usage: progName load writersCount readersCount");
// return;
// }
//
// int load = int.Parse(args[0]);
// int writersCount = int.Parse(args[1]);
// int readersCount = int.Parse(args[2]);
//
// TestLockMulti(load, writersCount, readersCount);
// TestLockMulti(load, writersCount, readersCount);
// TestReaderWriterMulti(load, writersCount, readersCount);
// TestReaderWriterMulti(load, writersCount, readersCount);
// monothreaded
// TestEmptyLoop(load);
// TestEmptyLoop(load);
// TestLock(load);
// TestLock(load);
// TestReaderWriterLockSlim_Read(load);
// TestReaderWriterLockSlim_Read(load);
// TestReaderWriterLockSlim_Write(load);
// TestReaderWriterLockSlim_Write(load);
}
#region multithreaded during the fixed time
private static void TestLockMultiTime(int millis, int writersCount, int readersCount)
{
long reads = 0;
long writes = 0;
object obj = new object();
bool flag = true;
string b;
ThreadStart writer = delegate()
{
while (flag)
{
lock (obj)
{
long localW = Interlocked.Increment(ref writes);
localW %= dictionary.Count;
dictionary[localW.ToString()] = localW.ToString();
}
MakeTemporization(false);
}
};
ThreadStart reader = delegate()
{
while (flag)
{
lock (obj)
{
long localW = Interlocked.Increment(ref reads);
localW %= dictionary.Count;
b = dictionary[localW.ToString()];
}
MakeTemporization(true);
}
};
Stopwatch start = Stopwatch.StartNew();
// start threads
IAsyncResult[] writerResults = StartThreads(writersCount, writer);
IAsyncResult[] readerResults = StartThreads(readersCount, reader);
Thread.Sleep(millis);
flag = false;
// wait for the execution
WaitThreads(writer, writerResults);
WaitThreads(reader, readerResults);
long elapsed = start.ElapsedMilliseconds;
System.Console.WriteLine(
string.Format(
"TestLockMultiTime: timer {0}ms, readersCount {1}, writersCount {2}, reads {3}, writes {4}, readLock {5}ns, writeLock {6}ns",
millis, readersCount, writersCount, reads, writes, ((double)millis) / reads * 1000000, ((double)millis) / writes * 1000000));
}
private static void TestReaderWriterMultiTime(int millis, int writersCount, int readersCount)
{
long reads = 0;
long writes = 0;
ReaderWriterLockSlim LOCK = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
bool flag = true;
string b;
ThreadStart writer = delegate()
{
while (flag)
{
LOCK.EnterWriteLock();
try
{
long localW = Interlocked.Increment(ref writes);
localW %= dictionary.Count;
dictionary[localW.ToString()] = localW.ToString();
}
finally
{
LOCK.ExitWriteLock();
}
MakeTemporization(false);
}
};
ThreadStart reader = delegate()
{
while (flag)
{
LOCK.EnterReadLock();
try
{
long localW = Interlocked.Increment(ref reads);
localW %= dictionary.Count;
b = dictionary[localW.ToString()];
}
finally
{
LOCK.ExitReadLock();
}
MakeTemporization(true);
}
};
Stopwatch start = Stopwatch.StartNew();
// start threads
IAsyncResult[] writerResults = StartThreads(writersCount, writer);
IAsyncResult[] readerResults = StartThreads(readersCount, reader);
Thread.Sleep(millis);
flag = false;
// wait for the execution
WaitThreads(writer, writerResults);
WaitThreads(reader, readerResults);
long elapsed = start.ElapsedMilliseconds;
System.Console.WriteLine(
string.Format(
"TestReaderWriterMultiTime: timer {0}ms, readersCount {1}, writersCount {2}, reads {3}, writes {4}, readLock {5}ns, writeLock {6}ns",
millis, readersCount, writersCount, reads, writes, ((double)millis) / reads * 1000000, ((double)millis) / writes * 1000000));
}
private static void TestFastResourceLockMultiTime(int millis, int writersCount, int readersCount)
{
long reads = 0;
long writes = 0;
FastResourceLock LOCK = new FastResourceLock();
bool flag = true;
string b;
ThreadStart writer = delegate()
{
while (flag)
{
LOCK.AcquireExclusive();
try
{
long localW = Interlocked.Increment(ref writes);
localW %= dictionary.Count;
dictionary[localW.ToString()] = localW.ToString();
}
finally
{
LOCK.ReleaseExclusive();
}
MakeTemporization(false);
}
};
ThreadStart reader = delegate()
{
while (flag)
{
LOCK.AcquireShared();
try
{
long localW = Interlocked.Increment(ref reads);
localW %= dictionary.Count;
b = dictionary[localW.ToString()];
}
finally
{
LOCK.ReleaseShared();
}
MakeTemporization(true);
}
};
Stopwatch start = Stopwatch.StartNew();
// start threads
IAsyncResult[] writerResults = StartThreads(writersCount, writer);
IAsyncResult[] readerResults = StartThreads(readersCount, reader);
Thread.Sleep(millis);
flag = false;
// wait for the execution
WaitThreads(writer, writerResults);
WaitThreads(reader, readerResults);
long elapsed = start.ElapsedMilliseconds;
System.Console.WriteLine(
string.Format(
"TestFastResourceLockMultiTime: timer {0}ms, readersCount {1}, writersCount {2}, reads {3}, writes {4}, readLock {5}ns, writeLock {6}ns",
millis, readersCount, writersCount, reads, writes, ((double)millis) / reads * 1000000, ((double)millis) / writes * 1000000));
}
private static int sleepCounter;
private static void MakeTemporization(bool read)
{
// 1- implementation 1: write to console
// System.Console.WriteLine(" ");
// 2- implementation 2: sleepint step = read ? 2000 : 20;
int localSleepCounter = Interlocked.Increment(ref sleepCounter);
if (localSleepCounter % step == 0)
{
Thread.Sleep(1);
}
else
{
Thread.Sleep(0);
//SwitchToThread();
}
// 3- implementation: switch the thread
// SwitchToThread();
}
}
M>TestLockMultiTime: timer 5000ms, readersCount 50, writersCount 1, reads 6630345, writes 59657, readLock 754.108572027549ns, writeLock 83812.4612367367ns M>TestReaderWriterMultiTime: timer 5000ms, readersCount 50, writersCount 1, reads 8298059, writes 7583, readLock 602.550548266769ns, writeLock 659369.642621654ns M>TestFastResourceLockMultiTime: timer 5000ms, readersCount 50, writersCount 1, reads 8927731, writes 14211, readLock 560.052716642112ns, writeLock 351840.123847724ns M>TestReaderWriterMultiTime, TestFastResourceLockMultiTime — 100% of CPU !!!! with 50 readers
M>TestLockMultiTime: timer 5000ms, readersCount 10, writersCount 1, reads 5959031, writes 80969, readLock 839.062592559092ns, writeLock 61752.028554138ns M>TestReaderWriterMultiTime: timer 5000ms, readersCount 10, writersCount 1, reads 6192030, writes 35970, readLock 807.489627795731ns, writeLock 139004.726160689ns M>TestFastResourceLockMultiTime: timer 5000ms, readersCount 10, writersCount 1, reads 6318584, writes 77415, readLock 791.316535476936ns, writeLock 64586.9663501905ns
M>FastResourceLock делает в 2 раза больше записей чем ReaderWriterLockSlim, оба делают чуть больше чтений но проигрывают сильно в количестве записей lock-у
Смотри показатели для ReaderWriterLock для 11 потоков значительно улучшаются. 51 поток это неэфективно для двухядерного процессора, с гипертриденгом, ИМХО, как раз 4-6 ну может 8 потоков и должно работать для максимальной отдачи.
Я думаю на 4 потоках ReaderWriterLockSlim покажет свою реальную область применения — процент чтений/записей при которых он выгоднее чем лок. Хотя меня расстраивает, что такая красивая идея не рвет в клочья лок во всех случаях.
Не совсем понял твой вывод по FastResourceLock, ведь он сделал на 300К больше чтений чем лок, по записи проиграл 4.5К но цифры там сопоставимы — я бы его признал победителем для 11 потоков.
M>кроме того TestReaderWriterMultiTime и TestFastResourceLockMultiTime кушают 100% процессора на 50 threads, lock — около нуля
Ну понятно, что лок запихнул все потоки в очередь, и достает по одному. Я понимаю что хочеться протестировать на запредельных для реального приложения параметрах, но 50 потоков это совсем другой случай — в действительности они же не будут работать паралельно...
Здравствуйте, mihhon, Вы писали:
M>не могу никак придумать пример, на котором был бы выигрыш от ReaderWriterLockSlim по сравнению с lock более существенный, чем в 2 раза при чтении (часто при существенном при проигрыше в записи), это нормально ? больше не получится? M>при том оверхед от ReaderWriterLockSlim в 2 раза больше
А можно для тупых объяснить, каким образом вы при помощи lock разрешите одновременный доступ на чтение?
Весь смысл ReaderWriterLock в том, чтобы 1000 читателей не выстраивались в очередь, а читали одновременно.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>А можно для тупых объяснить, каким образом вы при помощи lock разрешите одновременный доступ на чтение? S>Весь смысл ReaderWriterLock в том, чтобы 1000 читателей не выстраивались в очередь, а читали одновременно.
Где взять 1000 читателей на десктопе?! Теория вопросов не вызывает — красиво, но я так понял автор ищет реальный общий случай для дот .нет приложений для которого можно сказать: "здесь нужно использовать ReaderWriterLockSlim"...
Dictonary хороший кандидат, на такую потокобезопасную обертку.
Re[2]: lock ws ReaderWriterLockSlim: интерес?
От:
Аноним
Дата:
22.01.11 09:32
Оценка:
S>А можно для тупых объяснить, каким образом вы при помощи lock разрешите одновременный доступ на чтение? S>Весь смысл ReaderWriterLock в том, чтобы 1000 читателей не выстраивались в очередь, а читали одновременно.
тупой может написать кусок кода (примеры экзотического программинга, типа распечатки на принтер энциклопедии из залоченной секции, не берём, реалистичный пример — запись/чтение в Dictionary в залоченной секции), который показывает, что ReaderWriterLockSlim выгоднее lock и закопипастить его сюда?
Здравствуйте, vf, Вы писали:
vf>Где взять 1000 читателей на десктопе?!
1. А почему сразу на десктопе?
2. Что будем делать через пять-десять лет, когда ядер будет больше, чем сейчас мегабайт?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
vf>>Где взять 1000 читателей на десктопе?! S>1. А почему сразу на десктопе?
Потому что я считаю что большинство дот нет серверов — это "адвансед десктоп машина", и число ядер там отличается в разы, но никак не на 3 порядка, что ситуацию не меняет.
S>2. Что будем делать через пять-десять лет, когда ядер будет больше, чем сейчас мегабайт?
Я ждал этого аргумента Софт развивается тоже очень быстро, если "ядер будет больше, чем сейчас мегабайт" будет другое более адекватное решение. А вообще я голосую за лок-фри!
D>Цифирь итоговая от запуска к запуску может влёгкую на порядок различаться.
не различалась
D>В том и дело, что нет там никакой очереди. lock успевает отработать за квант, а ядер всего два, и ThreadPool не стартует worker'ов больше чем ядер. В итоге блокировок практически не бывает.
ThreadPool стартует новые threads, если количество задач в очереди не меняется в течение 0,5секунды, тесты по 5секунд, т.е. к концу теста 11-12 threads
D>Как там автор нулевую загрузку увидел — не понимаю. Она — после "разогрева" ThreadPool'а — тоже должна быть около 100%. Разве что у автора нетбук
не знаю, посмотрю на большем отрезке времени
M>ThreadPool стартует новые threads, если количество задач в очереди не меняется в течение 0,5секунды, тесты по 5секунд, т.е. к концу теста 11-12 threads
Да нипричём тут количество потоков. Просто в Вашем "тесте" lock'а блокировки практически никогда не возникают. Происходит это бо шаг Вашего цикла нагрузки почти в точности равен кванту — загадочный метод MakeTemporization() обеспечивает это явным образом. Посему когда следующий поток отправляется на исполнение — lock уже свободен.
D>>Как там автор нулевую загрузку увидел — не понимаю. Она — после "разогрева" ThreadPool'а — тоже должна быть около 100%. Разве что у автора нетбук M>не знаю, посмотрю на большем отрезке времени
vf>Смотри показатели для ReaderWriterLock для 11 потоков значительно улучшаются. 51 поток это неэфективно для двухядерного процессора, с гипертриденгом, ИМХО, как раз 4-6 ну может 8 потоков и должно работать для максимальной отдачи.
камень в огород FastResourceLock: на 2 х Хeon L5520 — hp proliant dl360 g6, на разогретом ThreadPool
int threadNum = writersCount + readersCount;
ThreadPool.SetMinThreads(threadNum, threadNum);
ThreadPool.SetMaxThreads(threadNum, threadNum);
, на 50 читающих threads какая-то бага , блокируются все читатели хороший такой троянский конь в production может получиться
я поправил тест, ThreadPool сразу содержит требуемое количство threads, и размер пула не меняется
MakeTemporization — это метод, который должен позволить моделизировать "реальную" нагрузку приложения: чтоб количество записей было в XXX раз меньше чтений , я не знаю как лучше его реализовать : Thread.Sleep(1) с учётом точности таймера не самое лучшее решение. я поправил его : убрал Thread.Sleep(0).
в итоге: если смотреть на цифры, я не вижу никакой выгоды от ReaderWriterLockSlim: он всё время поразывает худший результат, чем lock, а FastResourceLock , похоже с багами
начальный вопрос: когда же выгоднее использовать ReaderWriterLockSlim, сколько процессоров и какое соотношение чтений/записей ?
Здравствуйте, mihhon, Вы писали:
M>FastResourceLock , похоже с багами
When that thread is done with the lock, it clears the L bit (so other threads can set it to acquire the lock). If the thread sees that there is an exclusive waiter (by looking at EW), it releases the semaphore once, waking up an exclusive waiter. If there are no exclusive waiters but there are shared waiters, it releases all of them at once (since they can all acquire the lock at the same time – that is the whole point of a reader-writer lock).
Там нет очереди... Это не баг, это особенность. Думаю при желании такое поведение можно легко изменить, хотя 50 писателей и 1 читатель не совсем та ситуации, а этот механизм без претензий на универсальность?!