Что может вызвать большие накладные расходы при выполнении вызова HttpWebRequest?

Когда я отправляю / получаю данные с помощью HttpWebRequestSilverlight) небольшими блоками, я измеряю очень маленькую пропускную способность в 500 байт / с за соединение "localhost". При отправке данных большими блоками я получаю 2 МБ / с, что примерно в 5000 раз быстрее.

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

Дополнительная информация:

  • Я использую метод HTTP POST
  • Я измерил производительность как в Firefox 3.6, так и в Internet Explorer 7. Оба показали похожие результаты.
  • Мой процессор загружен всего на 10% (четырехъядерный, то есть на самом деле 40%)
  • WebClient показал аналогичные результаты
  • WCF / SOAP показал аналогичные результаты

Обновление. Используемый мной клиентский код Silverlight по сути является моей собственной реализацией класса WebClient. Причина, по которой я написал это, заключается в том, что я заметил ту же проблему производительности с WebClient, и я подумал, что HttpWebRequest позволит настроить проблему производительности. К сожалению, это не сработало. Реализация следующая:

public class HttpCommChannel
{
    public delegate void ResponseArrivedCallback(object requestContext, BinaryDataBuffer response);

    public HttpCommChannel(ResponseArrivedCallback responseArrivedCallback)
    {
        this.responseArrivedCallback = responseArrivedCallback;
        this.requestSentEvent = new ManualResetEvent(false);
        this.responseArrivedEvent = new ManualResetEvent(true);
    }

    public void MakeRequest(object requestContext, string url, BinaryDataBuffer requestPacket)
    {
        responseArrivedEvent.WaitOne();
        responseArrivedEvent.Reset();

        this.requestMsg = requestPacket;
        this.requestContext = requestContext;

        this.webRequest = WebRequest.Create(url) as HttpWebRequest;
        this.webRequest.AllowReadStreamBuffering = true;
        this.webRequest.ContentType = "text/plain";
        this.webRequest.Method = "POST";

        this.webRequest.BeginGetRequestStream(new AsyncCallback(this.GetRequestStreamCallback), null);
        this.requestSentEvent.WaitOne();
    }

    void GetRequestStreamCallback(IAsyncResult asynchronousResult)
    {
        System.IO.Stream postStream = webRequest.EndGetRequestStream(asynchronousResult);

        postStream.Write(requestMsg.Data, 0, (int)requestMsg.Size);
        postStream.Close();

        requestSentEvent.Set();
        webRequest.BeginGetResponse(new AsyncCallback(this.GetResponseCallback), null);
    }

    void GetResponseCallback(IAsyncResult asynchronousResult)
    {
        HttpWebResponse response = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult);
        Stream streamResponse = response.GetResponseStream();
        Dim.Ensure(streamResponse.CanRead);
        byte[] readData = new byte[streamResponse.Length];
        Dim.Ensure(streamResponse.Read(readData, 0, (int)streamResponse.Length) == streamResponse.Length);
        streamResponse.Close();
        response.Close();

        webRequest = null;
        responseArrivedEvent.Set();
        responseArrivedCallback(requestContext, new BinaryDataBuffer(readData));
    }

    HttpWebRequest webRequest;
    ManualResetEvent requestSentEvent;
    BinaryDataBuffer requestMsg;
    object requestContext;
    ManualResetEvent responseArrivedEvent;
    ResponseArrivedCallback responseArrivedCallback;
}

Я использую этот код для отправки данных на HTTP-сервер и обратно.

Обновление: после обширных исследований я пришел к выводу, что проблема производительности присуща Silverlight v3.


person Dimitri C.    schedule 08.03.2010    source источник
comment
Покажите, пожалуйста, код - очень сложно понять, что происходит, не видя кода.   -  person Jon Skeet    schedule 08.03.2010


Ответы (3)


Вполне возможно, что вы наблюдаете эффект алгоритма Нэгла, попробуйте:

this.webRequest.UseNagleAlgorithm.ServicePoint = false;

Кроме того, «рукопожатие» Expect100Continue имеет отношение к производительности вызова службы мыла:

this.webRequest.Expect100Continue.ServicePoint = false;

UDPATE:

Только что понял, что ServicePoint недоступен в Compact Framework. Однако вы можете доказать свою точку зрения, выполнив:

ServicePointManager.UseNagleAlgorithm = false

Или изменить соответствующий параметр в файле конфигурации приложения, или что-то еще в Silverlight?

person redcalx    schedule 08.03.2010
comment
не доступен в CF: что вы имеете в виду под CF? - person Dimitri C.; 08.03.2010
comment
Извините, меня путали Compact Framework с Silverlight. Поэтому я не уверен, что объект ServicePoint доступен в silverlight до фактического выполнения HTTP-запроса с помощью GetResponse (). В CF этот объект создается поздно, чтобы уменьшить использование ресурсов. - person redcalx; 08.03.2010
comment
Хотя я не могу это проверить, вы, вероятно, правы насчет задержки Нэгла. Я не думал об этом. К сожалению, похоже, что UseNagleAlgorithm недоступен в Silverlight. В любом случае, большое спасибо за ваш ответ! По крайней мере, теперь я понимаю проблему. - person Dimitri C.; 08.03.2010
comment
Вау, это возвращает воспоминания. Мы столкнулись с этой проблемой 12 лет назад; мы отправляли пакеты обновления среды с частотой 16 Гц, но были удивлены, увидев, что они приходили группами по четыре с частотой 4 Гц. К счастью для нас, мы использовали C ++ в Linux, поэтому был флаг, который мы могли передать сокету, чтобы отключить его. Не знали, что это называется алгоритмом Нэгла, но мы всегда называли это объединением пакетов. - person Mike D.; 09.03.2010

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

По сути, любой интерфейс, который пересекает границу коммуникации - будь то процесс или машина - должен быть «коротким», а не «болтливым». Отправляйте как можно больше информации в каждом запросе и получайте как можно больше данных в ответ. Это может показаться тривиальным на той же машине, но я увидел десятикратное улучшение производительности на сервере приложений за счет пакетирования команд в рабочем процессе, вместо того, чтобы выполнять обратный вызов из рабочего процесса в процесс главного сервера для каждой команды.

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

person Mike Dimmick    schedule 08.03.2010
comment
используйте большие размеры блоков: я не могу понять фактор 5000, который мне кажется абсурдно высоким. - person Dimitri C.; 08.03.2010

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

person saret    schedule 08.03.2010