Ответ 1
Обновление: прочитайте до конца. Похоже, что таблица поиска работает быстрее, чем Math.Sin.
Я предполагаю, что подход поиска будет быстрее, чем Math.Sin. Я также сказал бы, что это было бы намного быстрее, но ответ Роберта заставил меня думать, что я все еще хотел бы проверить это, чтобы быть уверенным. Я занимаюсь обработкой аудио-буфера и заметил, что такой метод:
for (int i = 0; i < audiodata.Length; i++)
{
audiodata[i] *= 0.5;
}
будет выполняться значительно быстрее, чем
for (int i = 0; i < audiodata.Length; i++)
{
audiodata[i] = Math.Sin(audiodata[i]);
}
Если разница между Math.Sin и простым умножением существенная, я бы предположил, что разница между Math.Sin и поиском также будет существенной.
Я не знаю, хотя, и мой компьютер с Visual Studio находится в подвале, и я слишком устал, чтобы потратить 2 минуты, чтобы определить это.
Обновление: ОК, тестирование заняло более 2 минут (более 20), но похоже, что Math.Sin как минимум в два раза быстрее таблицы поиска (с использованием словаря). Вот класс, который делает Sin, используя Math.Sin или таблицу поиска:
public class SinBuddy
{
private Dictionary<double, double> _cachedSins
= new Dictionary<double, double>();
private const double _cacheStep = 0.01;
private double _factor = Math.PI / 180.0;
public SinBuddy()
{
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += _cacheStep)
{
double angleRadians = angleDegrees * _factor;
_cachedSins.Add(angleDegrees, Math.Sin(angleRadians));
}
}
public double CacheStep
{
get
{
return _cacheStep;
}
}
public double SinLookup(double angleDegrees)
{
double value;
if (_cachedSins.TryGetValue(angleDegrees, out value))
{
return value;
}
else
{
throw new ArgumentException(
String.Format("No cached Sin value for {0} degrees",
angleDegrees));
}
}
public double Sin(double angleDegrees)
{
double angleRadians = angleDegrees * _factor;
return Math.Sin(angleRadians);
}
}
И вот код теста/синхронизации:
SinBuddy buddy = new SinBuddy();
System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
int loops = 200;
// Math.Sin
timer.Start();
for (int i = 0; i < loops; i++)
{
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += buddy.CacheStep)
{
double d = buddy.Sin(angleDegrees);
}
}
timer.Stop();
MessageBox.Show(timer.ElapsedMilliseconds.ToString());
// lookup
timer.Start();
for (int i = 0; i < loops; i++)
{
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += buddy.CacheStep)
{
double d = buddy.SinLookup(angleDegrees);
}
}
timer.Stop();
MessageBox.Show(timer.ElapsedMilliseconds.ToString());
Использование значения шага в 0,01 градуса и циклический просмотр всего диапазона значений 200 раз (как в этом коде) занимает около 1,4 секунды при использовании Math.Sin и около 3,2 секунды при использовании справочной таблицы словаря. Снижение значения шага до 0,001 или 0,0001 делает поиск еще хуже по сравнению с Math.Sin. Кроме того, этот результат еще более благоприятствует использованию Math.Sin, поскольку SinBuddy.Sin выполняет умножение для поворота угла в градусах в угол в радианах при каждом вызове, а SinBuddy.SinLookup просто выполняет прямой поиск.
Это на дешевом ноутбуке (без двухъядерных процессоров или чего-то необычного). Роберт, ты да человек! (Но я все еще думаю, что должен получить чек, потому что я сделал работу).
Обновление 2: ОК, я идиот... Оказывается, что остановка и перезапуск секундомера не сбрасывает истекшие миллисекунды, поэтому поиск только показался в два раза быстрее, потому что время включало время вызовов Math.Sin. Кроме того, я перечитал вопрос и понял, что вы говорите о кэшировании значений в простом массиве, а не об использовании словаря. Вот мой модифицированный код (я оставляю старый код как предупреждение для будущих поколений):
public class SinBuddy
{
private Dictionary<double, double> _cachedSins
= new Dictionary<double, double>();
private const double _cacheStep = 0.01;
private double _factor = Math.PI / 180.0;
private double[] _arrayedSins;
public SinBuddy()
{
// set up dictionary
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += _cacheStep)
{
double angleRadians = angleDegrees * _factor;
_cachedSins.Add(angleDegrees, Math.Sin(angleRadians));
}
// set up array
int elements = (int)(360.0 / _cacheStep) + 1;
_arrayedSins = new double[elements];
int i = 0;
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += _cacheStep)
{
double angleRadians = angleDegrees * _factor;
//_cachedSins.Add(angleDegrees, Math.Sin(angleRadians));
_arrayedSins[i] = Math.Sin(angleRadians);
i++;
}
}
public double CacheStep
{
get
{
return _cacheStep;
}
}
public double SinArrayed(double angleDegrees)
{
int index = (int)(angleDegrees / _cacheStep);
return _arrayedSins[index];
}
public double SinLookup(double angleDegrees)
{
double value;
if (_cachedSins.TryGetValue(angleDegrees, out value))
{
return value;
}
else
{
throw new ArgumentException(
String.Format("No cached Sin value for {0} degrees",
angleDegrees));
}
}
public double Sin(double angleDegrees)
{
double angleRadians = angleDegrees * _factor;
return Math.Sin(angleRadians);
}
}
И код теста/времени:
SinBuddy buddy = new SinBuddy();
System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
int loops = 200;
// Math.Sin
timer.Start();
for (int i = 0; i < loops; i++)
{
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += buddy.CacheStep)
{
double d = buddy.Sin(angleDegrees);
}
}
timer.Stop();
MessageBox.Show(timer.ElapsedMilliseconds.ToString());
// lookup
timer = new System.Diagnostics.Stopwatch();
timer.Start();
for (int i = 0; i < loops; i++)
{
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += buddy.CacheStep)
{
double d = buddy.SinLookup(angleDegrees);
}
}
timer.Stop();
MessageBox.Show(timer.ElapsedMilliseconds.ToString());
// arrayed
timer = new System.Diagnostics.Stopwatch();
timer.Start();
for (int i = 0; i < loops; i++)
{
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += buddy.CacheStep)
{
double d = buddy.SinArrayed(angleDegrees);
}
}
timer.Stop();
MessageBox.Show(timer.ElapsedMilliseconds.ToString());
Эти результаты совершенно разные. Использование Math.Sin занимает около 850 миллисекунд, таблица поиска в словаре - около 1300 миллисекунд, а таблица поиска на основе массива - около 600 миллисекунд. Таким образом, похоже, что (правильно написанная [gulp]) таблица поиска на самом деле немного быстрее, чем использование Math.Sin, но ненамного.
Пожалуйста, проверьте эти результаты самостоятельно, так как я уже продемонстрировал свою некомпетентность.