Оптимизировать цикл вокруг вызова get и функции, используя возвращаемое значение? - PullRequest
1 голос
/ 28 апреля 2011

Вот фрагмент, получающий данные из буферизованного источника и отправляющий их для обработки. Если очередь пуста, get () возвращает ноль, а метод процесса с удовольствием принимает ноль и ничего не делает. Какой самый оптимальный способ кодировать это?

something a; // any legal C++ return type...
aQueueOfSomethings g;

while (true) { 
    a=g.get();
    process(a);
}

Невозможно предсказать значения, поступающие с помощью get (), они являются такими, какие они есть, и их необходимо снять с очереди и передать в process () как можно быстрее.

Я не вижу здесь много напрасных усилий - если я пропущу явную локальную переменную с именем 'a' и сделаю цикл одним слоем:

    process(g.get());

неявное возвращаемое значение g.get () будет по-прежнему иметь выделенное пространство, может включать вызов конструктора и т. Д. И т. Д.

Если возвращаемая вещь имеет какой-либо размер или сложность, было бы лучше иметь указатель на нее, а не ее копию, и передавать этот указатель, а не копию по значению ... Поэтому я бы предпочел иметь

something *a;

    g.get(a);
    process(a);

вместо

 something a;

    a=g.get();
    process(a);

Я написал тестовый пример на c ++, пробуя две строчные и однострочные версии, цикл 100 000 000 раз.

Если a - это объект с 4 целыми и 2 числами с плавающей запятой, и метод process () касается их всех, двухстрочное решение на самом деле быстрее! Если объект является единственным int, однострочная версия работает быстрее. Если объект сложный, но метод process () касается только одного значения, однострочная версия работает быстрее.

Наиболее интересным для меня является то, что при использовании компилятора g ++, Mac OS X 10.5.8, переключатель оптимизации первого уровня -O обеспечивает идентичную, намного более быструю работу как с 1, так и с 2-строчной версией.

Кроме того, что компилятор может оптимизировать, одну строку для обоих методов и не указывать явную промежуточную переменную и передавать по ссылке, чтобы избежать копирования, есть ли что-нибудь, что обычно заставляло бы его работать быстрее? Я чувствую, что упускаю что-то очевидное.

Ответы [ 5 ]

3 голосов
/ 28 апреля 2011

Полагаю, вы пытаетесь победить компилятор на его собственной работе.

Были ли у вас проблемы с производительностью?Если нет, вы можете сосредоточиться на создании читаемого кода (который у вас, похоже, есть), который вы можете поддерживать, а не прибегать к тому, что может быть преждевременной оптимизацией, и загромождать код странными оптимизациями.

3 голосов
/ 28 апреля 2011

Я думаю, что это высший случай бесполезной оптимизации

(вы берете что-то, что буферизует и , хотите его оптимизировать по битам?)

Кроме того, компилятор будет компилировать оба пути в один и тот же код, и (в большинстве случаев) имеет полное право на оптимизацию возвращаемого значения и оптимизацию хвостового вызова.

В сочетании с вероятным встраиванием в queue_class :: get () ваша проблема выглядит полностью MOOT

2 голосов
/ 28 апреля 2011

Проблема с этим кодом не в том, что вы сделали, а в том, что он должен вращаться - тратит процессор циклов, которые могла бы выполнять какая-то другая задача, выполняемая вашим компьютером - даже когда нет работыделать.

Если существует много программ, которые придерживаются такой позиции (что они король компьютера и будут загружать целые процессоры), то все замедляется до абсолютного сканирования.Это очень радикальное решение, чтобы ваш код работал так.

Если возможно, измените всю модель так, чтобы вы получали обратный вызов / сигнал / событие , когда доступно больше данных..

1 голос
/ 28 апреля 2011

Вы правы, что вы должны позволить оптимизатору компилятора, но если вы знаете, что это безопасно сделать:

while (true) { 
    a=g.get();
    b=g.get();
    c=g.get();
    d=g.get();
    process(a);
    process(b);
    process(c);
    process(d);
}

, тогда это может ускорить процесс.

Илиеще более экстремально - подготовить целый массив возвращаемого типа (или указатели на него), а затем обработать их.Если process () и get () используют много кода, то это может означать, что весь код может оставаться в непосредственном кеше, а не извлекаться из дальнейшего кеша каждый раз, когда вызывается функция.

Компилятор не может выполнить эту оптимизацию, потому что он, вероятно, не знает, что безопасно переупорядочивать вызовы функций.

0 голосов
/ 28 апреля 2011

Это бесполезное усилие - вы делаете ненужный вызов оператора = (), когда говорите:

a=g.get();

Кроме того, что-то может не быть конструируемым по умолчанию, в этом случае вы не можете написатьпервая форма вообще.

...