Использование делегатов является чрезмерно плохой идеей для производительности? [дубликат]

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

content = ReplaceCallback.find(content, regex, new ReplaceCallback.Callback() {
    public String matches(MatchResult match) {
        // Do something special not normally allowed in regex's...
        return "newstring"
    }
});

весь список классов следует:

import java.util.regex.MatchResult;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.Stack;

/**
 * <p>
 * Class that provides a method for doing regular expression string replacement by passing the matched string to
 * a function that operates on the string.  The result of the operation is then used to replace the original match.
 * </p>
 * <p>Example:</p>
 * <pre>
 * ReplaceCallback.find("string to search on", "/regular(expression/", new ReplaceCallback.Callback() {
 *      public String matches(MatchResult match) {
 *          // query db or whatever...
 *          return match.group().replaceAll("2nd level replacement", "blah blah");
 *      }
 * });
 * </pre>
 * <p>
 * This, in effect, allows for a second level of string regex processing.
 * </p>
 *
 */
public class ReplaceCallback {
    public static interface Callback {
        public String matches(MatchResult match);
    }

    private final Pattern pattern;
    private Callback callback;

    private class Result {
        int start;
        int end;
        String replace;
    }

    /**
     * You probably don't need this.  {@see find(String, String, Callback)}
     * @param regex     The string regex to use
     * @param callback  An instance of Callback to execute on matches
     */
    public ReplaceCallback(String regex, final Callback callback) {
        this.pattern = Pattern.compile(regex);
        this.callback = callback;
    }

    public String execute(String string) {
        final Matcher matcher = this.pattern.matcher(string);
        Stack<Result> results = new Stack<Result>();
        while(matcher.find()) {
            final MatchResult matchResult = matcher.toMatchResult();
            Result r = new Result();
            r.replace = callback.matches(matchResult);
            if(r.replace == null)
                continue;
            r.start = matchResult.start();
            r.end = matchResult.end();
            results.push(r);
        }
        // Improve this with a stringbuilder...
        while(!results.empty()) {
            Result r = results.pop();
            string = string.substring(0, r.start) + r.replace + string.substring(r.end);
        }
        return string;
    }

    /**
     * If you wish to reuse the regex multiple times with different callbacks or search strings, you can create a
     * ReplaceCallback directly and use this method to perform the search and replace.
     *
     * @param string    The string we are searching through
     * @param callback  A callback instance that will be applied to the regex match results.
     * @return  The modified search string.
     */
    public String execute(String string, final Callback callback) {
        this.callback = callback;
        return execute(string);
    }

    /**
     * Use this static method to perform your regex search.
     * @param search    The string we are searching through
     * @param regex     The regex to apply to the string
     * @param callback  A callback instance that will be applied to the regex match results.
     * @return  The modified search string.
     */
    public static String find(String search, String regex, Callback callback) {
        ReplaceCallback rc = new ReplaceCallback(regex, callback);
        return rc.execute(search);
    }
}
5
задан andreialecu 13 August 2009 в 00:33
поделиться

7 ответов

Я надеялся получить некоторую документацию, касающуюся производительности в таких случаях, но, похоже, все, что я получил, это предложения о том, как улучшить мой код ... Кажется, никто не читал мой PS - нет баллов за вас.

Итак, я написал простой тестовый пример:

    public static bool IsDebuggingEnabled { get; set; }


    static void Main(string[] args)
    {
        for (int j = 0; j <= 10; j++)
        {
            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i <= 15000; i++)
            {
                Log(GetDebugMessage);
                if (i % 1000 == 0) IsDebuggingEnabled = !IsDebuggingEnabled;
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
        }

        Console.ReadLine();
        for (int j = 0; j <= 10; j++)
        {
            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i <= 15000; i++)
            {
                if (IsDebuggingEnabled) GetDebugMessage();
                if (i % 1000 == 0) IsDebuggingEnabled = !IsDebuggingEnabled;
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
        }
        Console.ReadLine();
    }

    public static string GetDebugMessage()
    {
        StringBuilder sb = new StringBuilder(100);
        Random rnd = new Random();
        for (int i = 0; i < 100; i++)
        {
            sb.Append(rnd.Next(100, 150));
        }
        return sb.ToString();
    }

    public static void Log(Func<string> getMessage)
    {
        if (IsDebuggingEnabled)
        {
            getMessage();
        }
    }

Кажется, что время между двумя версиями точно такое же. Я получаю 145 мс в первом случае и 145 мс во втором

Похоже, я ответил на свой вопрос.

3
ответ дан 14 December 2019 в 01:13
поделиться

Нет, у него не должно быть плохой производительности. В конце концов, вы будете вызывать его только в режиме отладки, где производительность не стоит на первом месте. Фактически, вы можете удалить лямбда и просто передать имя метода , чтобы убрать накладные расходы на ненужный промежуточный анонимный метод.

Обратите внимание, что если вы хотите сделать это в сборках отладки, вы можете добавить [Условный ("DEBUG")] атрибут метода журнала.

5
ответ дан 14 December 2019 в 01:13
поделиться

Вы также можете сделать это:

// no need for a lambda
instance.Log(GetDetailedDebugInfo)

// Using these instance methods on the logger
public void Log(Func<string> detailsProvider)
{
    if (!DebuggingEnabled)
        return;

    this.LogImpl(detailsProvider());
}

public void Log(string message)
{
    if (!DebuggingEnabled)
        return;

    this.LogImpl(message);
}

protected virtual void LogImpl(string message)
{
    ....
}
1
ответ дан 14 December 2019 в 01:13
поделиться

Стандартные ответы:

  • Если вам нужно это сделать, вы должны это сделать.
  • Зациклите это 10 ^ 9 раз посмотрите на секундомер, и он покажет вам, сколько наносекунд потребуется.
  • Если ваша программа большая, скорее всего, у вас есть более серьезные проблемы в другом месте.
0
ответ дан 14 December 2019 в 01:13
поделиться

Вызов делегата getMessage напрямую вместо вызова Invoke для него.

if(IsDebuggingEnabled)
{
  LogInternal(getMessage());
}

Вы также должны добавить нулевую проверку для getMessage.

0
ответ дан 14 December 2019 в 01:13
поделиться

Я считаю, что делегаты создают новый поток, так что вы можете быть правы насчет повышения производительности. Почему бы не настроить тестовый запуск, как предложил Дав, и внимательно следить за количеством потоков, порождаемых вашим приложением, для этого вы можете использовать Process Explorer.

Подождите! Меня поправили! Делегаты используют потоки только тогда, когда вы используете «BeginInvoke» ... поэтому мои комментарии выше не относятся к тому, как вы их используете.

-3
ответ дан 14 December 2019 в 01:13
поделиться

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

Сказав это для вашего первого примера:

if (IsDebuggingEnabled) 
{ 
    instance.Log(GetDetailedDebugInfo()); 
}

Если IsDebuggingEnabled статичен только для чтения, тогда проверка будет отклонена, поскольку она знает, что это никогда не изменится. Это означает, что приведенный выше образец не повлияет на производительность, если IsDebuggingEnabled имеет значение false, потому что после завершения JIT код исчезнет.

instance.Log(() => GetDetailedDebugInfo());

public void Log(Func<string> getMessage)
{
    if (IsDebuggingEnabled) 
    {
        LogInternal(getMessage.Invoke());
    }
}

Метод будет вызываться каждый раз при вызове instance.Log. Что будет медленнее.

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

3
ответ дан 14 December 2019 в 01:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: