Computer368106 views
高校倫理1440106 views
高校物理160203 views
数学講師2886227 views
小学算数1200427 views
教育149510 views
高校日本史190571 views
いろは3010814 views
中学英語811710 views
高校化学2924392 views
Help
Tools
NewsSpreadsheetCalendarSlidesTier ListPen ToolIllustrationCrayonPixel ArtASCII ArtPerspectiveEndless StairsGraphMind MapER DiagramFamily TreeMemeCurved TextImage EditorMosaicRetro FilterPencil SketchOCR/HighlighterMakeup EditorFaviconVideo TrimmerScrolling VideoVideo TitleColor PickerColor ExtractorBonfireFireworksWater RippleWater SplashBreaking GlassWood GrainMarble TextureCSS ButtonIcon MakerBar ChartGrouped Bar ChartStacked Bar ChartPie ChartLine ChartArea ChartStacked Area ChartScatter Plot3D Bar Chart3D Pie ChartBar Chart RaceBubble ChartPopulation PyramidPictogramEarningsCandlestick ChartInvestment RiskMortgage SimulatorCalculatorMatrix CalculatorFunction GraphPolynomial ExpansionVenn DiagramField VisualizerRubik's Cube Group TheoryTraveling SalesmanVoronoi and DelaunayFractalColumn ArithmeticDraw Math FiguresArithmetic AnimationArithmetic Word ProblemsCounting with Tree DiagramsCube NetsCross SectionsMotion PathMechanicsWavesLight and LensesThermodynamicsHow Semiconductors WorkMolecular StructuresAtomic OrbitalsElectrochemical CellsChemical EquilibriumCrystal LatticesBuffer pHComplex IonsDNA Double HelixCell DivisionMembrane ChannelsNerve ImpulseRock ClassificationWeatherConstellationsSolar and Lunar Eclipses3D ModelingFloor PlanSeismic StructuresIntersection TurnMaglevCooking AnimationOrigamiLive Viewer CountGeoJSON MapRailway MapPopulation MapCrime MapLand Price MapSchool MapShrine and Castle MapHouse of Representatives MapWord MapSolitaireReversiHakoiri MusumeChessHamburgerRippleSlide Puzzle MakerNeon PinballNovel MakerJapanese Typing PracticePiano Score EditorMusic Theory

English

sendfile でゼロコピー転送する

大きなファイルをネットワーク経由で送信する場合、sendfile システムコールを使うとカーネル空間だけでデータを転送でき、ユーザー空間へのコピーを省略できる。これをゼロコピー転送と呼ぶ。

通常のファイル送信の問題

通常の方法では、データがカーネル空間とユーザー空間を何度も行き来する。

# ❌ 効率が悪い(4回のコピー)
with open('large_file.bin', 'rb') as f:
    data = f.read()  # カーネル → ユーザー空間(コピー1, 2)
    socket.sendall(data)  # ユーザー空間 → カーネル(コピー3, 4)

この方法では、CPU とメモリの帯域幅を無駄に消費する。

os.sendfile() を使う

Python 3.3 以降、os.sendfile() でゼロコピー転送ができる(Unix 系のみ)。

import os
import socket

def send_file_zero_copy(sock, filepath):
    with open(filepath, 'rb') as f:
        # ファイルサイズを取得
        file_size = os.fstat(f.fileno()).st_size
        
        # sendfile でゼロコピー転送
        sent = 0
        while sent < file_size:
            sent += os.sendfile(
                sock.fileno(),  # 送信先ソケット
                f.fileno(),     # 送信元ファイル
                sent,           # オフセット
                file_size - sent  # 送信バイト数
            )
    
    return sent

os.sendfile() はカーネル空間内でファイルからソケットに直接データを転送する。

socket.sendfile() を使う(推奨)

Python 3.5 以降では、socket.sendfile() がより使いやすい。

import socket

def send_file_easy(sock, filepath):
    with open(filepath, 'rb') as f:
        sock.sendfile(f)

内部で os.sendfile() を使用し、フォールバック処理も行ってくれる。

HTTP サーバーでの例

簡易的な HTTP サーバーでファイルを効率的に送信する例を示す。

import socket
import os

def serve_file(client_sock, filepath):
    file_size = os.path.getsize(filepath)
    
    # HTTP ヘッダーを送信
    header = (
        f'HTTP/1.1 200 OK\r\n'
        f'Content-Length: {file_size}\r\n'
        f'Content-Type: application/octet-stream\r\n'
        f'\r\n'
    )
    client_sock.sendall(header.encode())
    
    # ファイル本体をゼロコピーで送信
    with open(filepath, 'rb') as f:
        client_sock.sendfile(f)

shutil.copyfileobj() との比較

shutil.copyfileobj() はユーザー空間でコピーを行う。

import shutil

# ユーザー空間コピー(チャンク単位)
with open('src.bin', 'rb') as src, open('dst.bin', 'wb') as dst:
    shutil.copyfileobj(src, dst, length=1024*1024)

ファイル間のコピーには shutil.copy() が使えるが、内部的にはユーザー空間を経由する。

copy_file_range() を使う(Linux 4.5+)

Linux 4.5 以降では os.copy_file_range() でファイル間のゼロコピーコピーができる。

import os

def zero_copy_file(src_path, dst_path):
    with open(src_path, 'rb') as src:
        with open(dst_path, 'wb') as dst:
            src_size = os.fstat(src.fileno()).st_size
            copied = 0
            
            while copied < src_size:
                copied += os.copy_file_range(
                    src.fileno(),
                    dst.fileno(),
                    src_size - copied,
                    copied,  # src_offset
                    copied   # dst_offset
                )

splice() の代替

Unix の splice() システムコールは Python 標準ライブラリにはないが、ctypes で呼び出すことができる。通常は sendfile() で十分だ。

Windows での制限

os.sendfile() は Windows では利用できない。Windows では TransmitFile API を使う必要があるが、Python 標準ライブラリではサポートされていない。

import os
import sys

def send_file_cross_platform(sock, filepath):
    with open(filepath, 'rb') as f:
        if hasattr(sock, 'sendfile') and sys.platform != 'win32':
            sock.sendfile(f)
        else:
            # フォールバック:チャンク単位で送信
            while chunk := f.read(65536):
                sock.sendall(chunk)

パフォーマンス比較

大きなファイル(数 GB)の転送では、ゼロコピーにより数十パーセントの性能向上が見込める。特に CPU 使用率が大幅に下がる。

方式CPU 使用率スループット
read() + send()高い標準
sendfile()低い高い

ただし、小さなファイルや低速なネットワークでは差が出にくい。